diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..43369eea55459685633f4a95743b60834dad2d74 --- /dev/null +++ b/.gitignore @@ -0,0 +1,64 @@ +# Virtualenv +/.venv/ +/venv/ + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# json file +*.json + +# Distribution / packaging +/bin/ +/build/ +/develop-eggs/ +/dist/ +/eggs/ +/lib/ +/lib64/ +/output/ +/parts/ +/sdist/ +/var/ +/*.egg-info/ +/.installed.cfg +/*.egg +/.eggs + +# AUTHORS and ChangeLog will be generated while packaging +/AUTHORS +/ChangeLog + +# BCloud / BuildSubmitter +/build_submitter.* +/logger_client_log + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +.tox/ +.coverage +.cache +.pytest_cache +nosetests.xml +coverage.xml + +# Translations +*.mo + +# Sphinx documentation +/docs/_build/ + +*.json + + +dataset/coco/annotations +dataset/coco/train2017 +dataset/coco/val2017 +dataset/voc/VOCdevkit diff --git a/.style.yapf b/.style.yapf new file mode 100644 index 0000000000000000000000000000000000000000..4741fb4f3bbc6681088cf9e960321e7b857a93a8 --- /dev/null +++ b/.style.yapf @@ -0,0 +1,3 @@ +[style] +based_on_style = pep8 +column_limit = 80 diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..3edd7a2fea40c2fa28efb45b7020a563464010a1 --- /dev/null +++ b/README.md @@ -0,0 +1,134 @@ +English | [简体中文](README_cn.md) + +# PaddleDetection + +The goal of PaddleDetection is to provide easy access to a wide range of object +detection models in both industry and research settings. We design +PaddleDetection to be not only performant, production-ready but also highly +flexible, catering to research needs. + + +
+ +
+ + +## Introduction + +Features: + +- Production Ready: + + Key operations are implemented in C++ and CUDA, together with PaddlePaddle's +highly efficient inference engine, enables easy deployment in server environments. + +- Highly Flexible: + + Components are designed to be modular. Model architectures, as well as data +preprocess pipelines, can be easily customized with simple configuration +changes. + +- Performance Optimized: + + With the help of the underlying PaddlePaddle framework, faster training and +reduced GPU memory footprint is achieved. Notably, YOLOv3 training is +much faster compared to other frameworks. Another example is Mask-RCNN +(ResNet50), we managed to fit up to 4 images per GPU (Tesla V100 16GB) during +multi-GPU training. + +Supported Architectures: + +| | ResNet | ResNet-vd [1](#vd) | ResNeXt-vd | SENet | MobileNet | DarkNet | VGG | +| ------------------- | :----: | ----------------------------: | :--------: | :---: | :-------: | :-----: | :--: | +| Faster R-CNN | ✓ | ✓ | x | ✓ | ✗ | ✗ | ✗ | +| Faster R-CNN + FPN | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | +| Mask R-CNN | ✓ | ✓ | x | ✓ | ✗ | ✗ | ✗ | +| Mask R-CNN + FPN | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | +| Cascade Faster-RCNN | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | +| Cascade Mask-RCNN | ✓ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | +| RetinaNet | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | +| YOLOv3 | ✓ | ✗ | ✗ | ✗ | ✓ | ✓ | ✗ | +| SSD | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | ✓ | + +[1] [ResNet-vd](https://arxiv.org/pdf/1812.01187) models offer much improved accuracy with negligible performance cost. + +Advanced Features: + +- [x] **Synchronized Batch Norm**: currently used by YOLOv3. +- [x] **Group Norm** +- [x] **Modulated Deformable Convolution** +- [x] **Deformable PSRoI Pooling** + +**NOTE:** Synchronized batch normalization can only be used on multiple GPU devices, can not be used on CPU devices or single GPU device. + +## Get Started + +- [Installation guide](docs/INSTALL.md) +- [Quick start on small dataset](docs/QUICK_STARTED.md) +- [Guide to traing, evaluate and arguments description](docs/GETTING_STARTED.md) +- [Guide to preprocess pipeline and custom dataset](docs/DATA.md) +- [Introduction to the configuration workflow](docs/CONFIG.md) +- [Examples for detailed configuration explanation](docs/config_example/) +- [IPython Notebook demo](demo/mask_rcnn_demo.ipynb) +- [Transfer learning document](docs/TRANSFER_LEARNING.md) + +## Model Zoo + +- Pretrained models are available in the [PaddleDetection model zoo](docs/MODEL_ZOO.md). +- [Face detection models](configs/face_detection/README.md) +- [Pretrained models for pedestrian and vehicle detection](contrib/README.md) + +## Model compression + +- [ Quantification aware training example](slim/quantization) +- [ Pruning compression example](slim/prune) + +## Depoly + +- [Export model for inference depolyment](docs/EXPORT_MODEL.md) +- [C++ inference depolyment](inference/README.md) + +## Benchmark + +- [Inference benchmark](docs/BENCHMARK_INFER_cn.md) + + +## Updates + +#### 10/2019 + +- Face detection models included: BlazeFace, Faceboxes. +- Enrich COCO models, box mAP up to 51.9%. +- Add CACacascade RCNN, one of the best single model of Objects365 2019 challenge Full Track champion. +- Add pretrained models for pedestrian and vehicle detection. +- Support mixed-precision training. +- Add C++ inference depolyment. +- Add model compression examples. + +#### 2/9/2019 + +- Add retrained models for GroupNorm. + +- Add Cascade-Mask-RCNN+FPN. + +#### 5/8/2019 + +- Add a series of models ralated modulated Deformable Convolution. + +#### 7/29/2019 + +- Update Chinese docs for PaddleDetection +- Fix bug in R-CNN models when train and test at the same time +- Add ResNext101-vd + Mask R-CNN + FPN models +- Add YOLOv3 on VOC models + +#### 7/3/2019 + +- Initial release of PaddleDetection and detection model zoo +- Models included: Faster R-CNN, Mask R-CNN, Faster R-CNN+FPN, Mask + R-CNN+FPN, Cascade-Faster-RCNN+FPN, RetinaNet, YOLOv3, and SSD. + + +## Contributing + +Contributions are highly welcomed and we would really appreciate your feedback!! diff --git a/README_cn.md b/README_cn.md new file mode 100644 index 0000000000000000000000000000000000000000..7793d6495b1ddb22146fb73847ede6bd6d4f76f1 --- /dev/null +++ b/README_cn.md @@ -0,0 +1,122 @@ +[English](README.md) | 简体中文 + +# PaddleDetection + +PaddleDetection的目的是为工业界和学术界提供丰富、易用的目标检测模型。不仅性能优越、易于部署,而且能够灵活的满足算法研究的需求。 + +
+ +
+ + +## 简介 + +特性: + +- 易部署: + + PaddleDetection的模型中使用的核心算子均通过C++或CUDA实现,同时基于PaddlePaddle的高性能推理引擎可以方便地部署在多种硬件平台上。 + +- 高灵活度: + + PaddleDetection通过模块化设计来解耦各个组件,基于配置文件可以轻松地搭建各种检测模型。 + +- 高性能: + + 基于PaddlePaddle框架的高性能内核,在模型训练速度、显存占用上有一定的优势。例如,YOLOv3的训练速度快于其他框架,在Tesla V100 16GB环境下,Mask-RCNN(ResNet50)可以单卡Batch Size可以达到4 (甚至到5)。 + +支持的模型结构: + +| | ResNet | ResNet-vd [1](#vd) | ResNeXt-vd | SENet | MobileNet | DarkNet | VGG | +|--------------------|:------:|------------------------------:|:----------:|:-----:|:---------:|:-------:|:---:| +| Faster R-CNN | ✓ | ✓ | x | ✓ | ✗ | ✗ | ✗ | +| Faster R-CNN + FPN | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | +| Mask R-CNN | ✓ | ✓ | x | ✓ | ✗ | ✗ | ✗ | +| Mask R-CNN + FPN | ✓ | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | +| Cascade Faster-CNN | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | +| Cascade Mask-CNN | ✓ | ✗ | ✗ | ✓ | ✗ | ✗ | ✗ | +| RetinaNet | ✓ | ✗ | ✓ | ✗ | ✗ | ✗ | ✗ | +| YOLOv3 | ✓ | ✗ | ✗ | ✗ | ✓ | ✓ | ✗ | +| SSD | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | ✓ | + +[1] [ResNet-vd](https://arxiv.org/pdf/1812.01187) 模型提供了较大的精度提高和较少的性能损失。 + +扩展特性: + +- [x] **Synchronized Batch Norm**: 目前在YOLOv3中使用。 +- [x] **Group Norm** +- [x] **Modulated Deformable Convolution** +- [x] **Deformable PSRoI Pooling** + +**注意:** Synchronized batch normalization 只能在多GPU环境下使用,不能在CPU环境或者单GPU环境下使用。 + + +## 使用教程 + +- [安装说明](docs/INSTALL_cn.md) +- [快速开始](docs/QUICK_STARTED_cn.md) +- [训练、评估及参数说明](docs/GETTING_STARTED_cn.md) +- [数据预处理及自定义数据集](docs/DATA_cn.md) +- [配置模块设计和介绍](docs/CONFIG_cn.md) +- [详细的配置信息和参数说明示例](docs/config_example/) +- [IPython Notebook demo](demo/mask_rcnn_demo.ipynb) +- [迁移学习教程](docs/TRANSFER_LEARNING_cn.md) + +## 模型库 + +- [模型库](docs/MODEL_ZOO_cn.md) +- [人脸检测模型](configs/face_detection/README.md) +- [行人检测和车辆检测预训练模型](contrib/README_cn.md) + + +## 模型压缩 +- [量化训练压缩示例](slim/quantization) +- [剪枝压缩示例](slim/prune) + +## 推理部署 + +- [模型导出教程](docs/EXPORT_MODEL.md) +- [C++推理部署](inference/README.md) + +## Benchmark + +- [推理Benchmark](docs/BENCHMARK_INFER_cn.md) + + + +## 版本更新 + +### 10/2019 + +- 增加人脸检测模型BlazeFace、Faceboxes。 +- 丰富基于COCO的模型,精度高达51.9%。 +- 增加Objects365 2019 Challenge上夺冠的最佳单模型之一CACascade-RCNN。 +- 增加行人检测和车辆检测预训练模型。 +- 支持FP16训练。 +- 增加跨平台的C++推理部署方案。 +- 增加模型压缩示例。 + + +### 2/9/2019 +- 增加GroupNorm模型。 +- 增加CascadeRCNN+Mask模型。 + +#### 5/8/2019 +- 增加Modulated Deformable Convolution系列模型。 + +#### 7/22/2019 + +- 增加检测库中文文档 +- 修复R-CNN系列模型训练同时进行评估的问题 +- 新增ResNext101-vd + Mask R-CNN + FPN模型 +- 新增基于VOC数据集的YOLOv3模型 + +#### 7/3/2019 + +- 首次发布PaddleDetection检测库和检测模型库 +- 模型包括:Faster R-CNN, Mask R-CNN, Faster R-CNN+FPN, Mask + R-CNN+FPN, Cascade-Faster-RCNN+FPN, RetinaNet, YOLOv3, 和SSD. + +## 如何贡献代码 + +我们非常欢迎你可以为PaddleDetection提供代码,也十分感谢你的反馈。 diff --git a/configs/cascade_mask_rcnn_r50_fpn_1x.yml b/configs/cascade_mask_rcnn_r50_fpn_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..1d17f53c60e66061ab8a21d624f4191a15ee5a01 --- /dev/null +++ b/configs/cascade_mask_rcnn_r50_fpn_1x.yml @@ -0,0 +1,145 @@ +architecture: CascadeMaskRCNN +train_feed: MaskRCNNTrainFeed +eval_feed: MaskRCNNEvalFeed +test_feed: MaskRCNNTestFeed +use_gpu: true +max_iters: 180000 +snapshot_iter: 10000 +log_smooth_window: 20 +save_dir: output +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_cos_pretrained.tar +metric: COCO +weights: output/cascade_mask_rcnn_r50_fpn_1x/model_final/ +num_classes: 81 + +CascadeMaskRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: CascadeBBoxHead + bbox_assigner: CascadeBBoxAssigner + mask_assigner: MaskAssigner + mask_head: MaskHead + +ResNet: + depth: 50 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: affine_channel + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + anchor_generator: + aspect_ratios: [0.5, 1.0, 2.0] + variance: [1.0, 1.0, 1.0, 1.0] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + sampling_ratio: 2 + box_resolution: 7 + mask_resolution: 14 + +MaskHead: + dilation: 1 + conv_dim: 256 + num_convs: 4 + resolution: 28 + +CascadeBBoxAssigner: + batch_size_per_im: 512 + bbox_reg_weights: [10, 20, 30] + bg_thresh_hi: [0.5, 0.6, 0.7] + bg_thresh_lo: [0.0, 0.0, 0.0] + fg_fraction: 0.25 + fg_thresh: [0.5, 0.6, 0.7] + +MaskAssigner: + resolution: 28 + +CascadeBBoxHead: + head: CascadeTwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +CascadeTwoFCHead: + mlp_dim: 1024 + +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 + +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 diff --git a/configs/cascade_rcnn_r50_fpn_1x.yml b/configs/cascade_rcnn_r50_fpn_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..47c089c1d95a35aacdcc0766670dc984cb18dc32 --- /dev/null +++ b/configs/cascade_rcnn_r50_fpn_1x.yml @@ -0,0 +1,137 @@ +architecture: CascadeRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +max_iters: 90000 +snapshot_iter: 10000 +use_gpu: true +log_smooth_window: 20 +save_dir: output +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_cos_pretrained.tar +weights: output/cascade_rcnn_r50_fpn_1x/model_final +metric: COCO +num_classes: 81 + +CascadeRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: CascadeBBoxHead + bbox_assigner: CascadeBBoxAssigner + +ResNet: + norm_type: affine_channel + depth: 50 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + variant: b + +FPN: + min_level: 2 + max_level: 6 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + min_level: 2 + max_level: 6 + num_chan: 256 + rpn_target_assign: + rpn_batch_size_per_im: 256 + rpn_fg_fraction: 0.5 + rpn_positive_overlap: 0.7 + rpn_negative_overlap: 0.3 + rpn_straddle_thresh: 0.0 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + min_level: 2 + max_level: 5 + box_resolution: 7 + sampling_ratio: 2 + +CascadeBBoxAssigner: + batch_size_per_im: 512 + bbox_reg_weights: [10, 20, 30] + bg_thresh_lo: [0.0, 0.0, 0.0] + bg_thresh_hi: [0.5, 0.6, 0.7] + fg_thresh: [0.5, 0.6, 0.7] + fg_fraction: 0.25 + +CascadeBBoxHead: + head: CascadeTwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +CascadeTwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.02 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [60000, 80000] + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/cascade_rcnn_r50_fpn_1x_ms_test.yml b/configs/cascade_rcnn_r50_fpn_1x_ms_test.yml new file mode 100644 index 0000000000000000000000000000000000000000..c345aeedbe6f65fbe19aaf87ddbadf5ed567c38d --- /dev/null +++ b/configs/cascade_rcnn_r50_fpn_1x_ms_test.yml @@ -0,0 +1,177 @@ +architecture: CascadeRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +max_iters: 90000 +snapshot_iter: 10000 +use_gpu: true +log_smooth_window: 20 +save_dir: output +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_cos_pretrained.tar +weights: output/cascade_rcnn_r50_fpn_1x/model_final +metric: COCO +num_classes: 81 + +CascadeRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: CascadeBBoxHead + bbox_assigner: CascadeBBoxAssigner + +ResNet: + norm_type: affine_channel + depth: 50 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + variant: b + +FPN: + min_level: 2 + max_level: 6 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + min_level: 2 + max_level: 6 + num_chan: 256 + rpn_target_assign: + rpn_batch_size_per_im: 256 + rpn_fg_fraction: 0.5 + rpn_positive_overlap: 0.7 + rpn_negative_overlap: 0.3 + rpn_straddle_thresh: 0.0 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + min_level: 2 + max_level: 5 + box_resolution: 7 + sampling_ratio: 2 + +CascadeBBoxAssigner: + batch_size_per_im: 512 + bbox_reg_weights: [10, 20, 30] + bg_thresh_lo: [0.0, 0.0, 0.0] + bg_thresh_hi: [0.5, 0.6, 0.7] + fg_thresh: [0.5, 0.6, 0.7] + fg_fraction: 0.25 + +CascadeBBoxHead: + head: CascadeTwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +CascadeTwoFCHead: + mlp_dim: 1024 + +MultiScaleTEST: + score_thresh: 0.05 + nms_thresh: 0.5 + detections_per_im: 100 + enable_voting: true + vote_thresh: 0.9 + +LearningRate: + base_lr: 0.02 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [60000, 80000] + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 + 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 + - !MultiscaleTestResize + origin_target_size: 800 + origin_max_size: 1333 + target_size: + - 400 + - 500 + - 600 + - 700 + - 900 + - 1000 + - 1100 + - 1200 + max_size: 2000 + use_flip: true + - !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 + pad_to_stride: 32 + drop_last: false + num_workers: 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 new file mode 100755 index 0000000000000000000000000000000000000000..6d666470f6969c1d4d3a7a1c2a16f11642c904d6 --- /dev/null +++ b/configs/dcn/cascade_mask_rcnn_dcnv2_se154_vd_fpn_gn_s1x.yml @@ -0,0 +1,257 @@ +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 +weights: output/cascade_mask_rcnn_dcn_se154_vd_fpn_gn_s1x/model_final/ +metric: COCO +num_classes: 81 + +CascadeMaskRCNN: + backbone: SENet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: CascadeBBoxHead + bbox_assigner: CascadeBBoxAssigner + mask_assigner: MaskAssigner + mask_head: MaskHead + +SENet: + depth: 152 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + group_width: 4 + groups: 64 + norm_type: bn + freeze_norm: True + variant: d + dcn_v2_stages: [3, 4, 5] + std_senet: True + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + freeze_norm: False + norm_type: gn + +FPNRPNHead: + anchor_generator: + aspect_ratios: [0.5, 1.0, 2.0] + variance: [1.0, 1.0, 1.0, 1.0] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + box_resolution: 7 + sampling_ratio: 2 + mask_resolution: 14 + +MaskHead: + dilation: 1 + conv_dim: 256 + num_convs: 4 + resolution: 28 + norm_type: gn + +CascadeBBoxAssigner: + batch_size_per_im: 512 + bbox_reg_weights: [10, 20, 30] + bg_thresh_hi: [0.5, 0.6, 0.7] + bg_thresh_lo: [0.0, 0.0, 0.0] + fg_fraction: 0.25 + fg_thresh: [0.5, 0.6, 0.7] + +MaskAssigner: + resolution: 28 + +CascadeBBoxHead: + head: CascadeXConvNormHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +CascadeXConvNormHead: + norm_type: gn + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [240000, 280000] + - !LinearWarmup + start_factor: 0.01 + steps: 2000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 + sample_transforms: + - !DecodeImage + to_rgb: False + with_mixup: False + - !RandomFlipImage + is_mask_flip: true + is_normalized: false + prob: 0.5 + - !NormalizeImage + is_channel_first: false + is_scale: False + mean: + - 102.9801 + - 115.9465 + - 122.7717 + std: + - 1.0 + - 1.0 + - 1.0 + - !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 + max_size: 1600 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false + batch_transforms: + - !PadBatch + pad_to_stride: 32 + num_workers: 8 + +MaskRCNNEvalFeed: + batch_size: 1 + dataset: + dataset_dir: dataset/coco + annotation: annotations/instances_val2017.json + image_dir: val2017 + sample_transforms: + - !DecodeImage + to_rgb: False + with_mixup: False + - !NormalizeImage + is_channel_first: false + is_scale: False + mean: + - 102.9801 + - 115.9465 + - 122.7717 + std: + - 1.0 + - 1.0 + - 1.0 + - !ResizeImage + interp: 1 + target_size: + - 800 + max_size: 1333 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false + batch_transforms: + - !PadBatch + pad_to_stride: 32 + num_workers: 2 + +MaskRCNNTestFeed: + batch_size: 1 + dataset: + annotation: dataset/coco/annotations/instances_val2017.json + sample_transforms: + - !DecodeImage + to_rgb: False + with_mixup: False + - !NormalizeImage + is_channel_first: false + is_scale: False + mean: + - 102.9801 + - 115.9465 + - 122.7717 + std: + - 1.0 + - 1.0 + - 1.0 + - !Permute + channel_first: true + to_bgr: false + batch_transforms: + - !PadBatch + pad_to_stride: 32 + num_workers: 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 new file mode 100644 index 0000000000000000000000000000000000000000..ea0b375b6f87cbbc358b3b7ec213cdab16661e47 --- /dev/null +++ b/configs/dcn/cascade_mask_rcnn_dcnv2_se154_vd_fpn_gn_s1x_ms_test.yml @@ -0,0 +1,272 @@ +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 +weights: output/cascade_mask_rcnn_dcn_se154_vd_fpn_gn_s1x/model_final/ +metric: COCO +num_classes: 81 + +CascadeMaskRCNN: + backbone: SENet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: CascadeBBoxHead + bbox_assigner: CascadeBBoxAssigner + mask_assigner: MaskAssigner + mask_head: MaskHead + +SENet: + depth: 152 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + group_width: 4 + groups: 64 + norm_type: bn + freeze_norm: True + variant: d + dcn_v2_stages: [3, 4, 5] + std_senet: True + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + freeze_norm: False + norm_type: gn + +FPNRPNHead: + anchor_generator: + aspect_ratios: [0.5, 1.0, 2.0] + variance: [1.0, 1.0, 1.0, 1.0] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + box_resolution: 7 + sampling_ratio: 2 + mask_resolution: 14 + +MaskHead: + dilation: 1 + conv_dim: 256 + num_convs: 4 + resolution: 28 + norm_type: gn + +CascadeBBoxAssigner: + batch_size_per_im: 512 + bbox_reg_weights: [10, 20, 30] + bg_thresh_hi: [0.5, 0.6, 0.7] + bg_thresh_lo: [0.0, 0.0, 0.0] + fg_fraction: 0.25 + fg_thresh: [0.5, 0.6, 0.7] + +MaskAssigner: + resolution: 28 + +CascadeBBoxHead: + head: CascadeXConvNormHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +CascadeXConvNormHead: + norm_type: gn + +MultiScaleTEST: + score_thresh: 0.05 + nms_thresh: 0.5 + detections_per_im: 100 + enable_voting: true + vote_thresh: 0.9 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [240000, 280000] + - !LinearWarmup + start_factor: 0.01 + steps: 2000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 + sample_transforms: + - !DecodeImage + to_rgb: False + with_mixup: False + - !RandomFlipImage + is_mask_flip: true + is_normalized: false + prob: 0.5 + - !NormalizeImage + is_channel_first: false + is_scale: False + mean: + - 102.9801 + - 115.9465 + - 122.7717 + std: + - 1.0 + - 1.0 + - 1.0 + - !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 + max_size: 1600 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false + batch_transforms: + - !PadBatch + pad_to_stride: 32 + num_workers: 8 + +MaskRCNNEvalFeed: + batch_size: 1 + dataset: + dataset_dir: dataset/coco + annotation: annotations/instances_val2017.json + image_dir: val2017 + sample_transforms: + - !DecodeImage + to_rgb: False + - !NormalizeImage + is_channel_first: false + is_scale: False + mean: + - 102.9801 + - 115.9465 + - 122.7717 + std: + - 1.0 + - 1.0 + - 1.0 + - !MultiscaleTestResize + origin_target_size: 800 + origin_max_size: 1333 + target_size: + - 400 + - 500 + - 600 + - 700 + - 900 + - 1000 + - 1100 + - 1200 + max_size: 2000 + use_flip: true + - !Permute + channel_first: true + to_bgr: false + batch_transforms: + - !PadMSTest + pad_to_stride: 32 + # num_scale = (len(target_size) + 1) * (1 + use_flip) + num_scale: 18 + num_workers: 2 + +MaskRCNNTestFeed: + batch_size: 1 + dataset: + annotation: dataset/coco/annotations/instances_val2017.json + sample_transforms: + - !DecodeImage + to_rgb: False + - !NormalizeImage + is_channel_first: false + is_scale: False + mean: + - 102.9801 + - 115.9465 + - 122.7717 + std: + - 1.0 + - 1.0 + - 1.0 + - !Permute + channel_first: true + to_bgr: false + batch_transforms: + - !PadBatch + pad_to_stride: 32 + num_workers: 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 new file mode 100644 index 0000000000000000000000000000000000000000..93373adb3a7f72b64ba45996ef61c2a9d3da8414 --- /dev/null +++ b/configs/dcn/cascade_rcnn_dcn_r101_vd_fpn_1x.yml @@ -0,0 +1,139 @@ +architecture: CascadeRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +max_iters: 90000 +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 +weights: output/cascade_rcnn_dcn_r101_vd_fpn_1x/model_final +metric: COCO +num_classes: 81 + +CascadeRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: CascadeBBoxHead + bbox_assigner: CascadeBBoxAssigner + +ResNet: + norm_type: bn + depth: 101 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + variant: d + dcn_v2_stages: [3, 4, 5] + +FPN: + min_level: 2 + max_level: 6 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + min_level: 2 + max_level: 6 + num_chan: 256 + rpn_target_assign: + rpn_batch_size_per_im: 256 + rpn_fg_fraction: 0.5 + rpn_positive_overlap: 0.7 + rpn_negative_overlap: 0.3 + rpn_straddle_thresh: 0.0 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + min_level: 2 + max_level: 5 + box_resolution: 7 + sampling_ratio: 2 + +CascadeBBoxAssigner: + batch_size_per_im: 512 + bbox_reg_weights: [10, 20, 30] + bg_thresh_lo: [0.0, 0.0, 0.0] + bg_thresh_hi: [0.5, 0.6, 0.7] + fg_thresh: [0.5, 0.6, 0.7] + fg_fraction: 0.25 + +CascadeBBoxHead: + head: CascadeTwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +CascadeTwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.02 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [60000, 80000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/dcn/cascade_rcnn_dcn_r50_fpn_1x.yml b/configs/dcn/cascade_rcnn_dcn_r50_fpn_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..4c74bd877b644659812bbcab960a4ce4600277ef --- /dev/null +++ b/configs/dcn/cascade_rcnn_dcn_r50_fpn_1x.yml @@ -0,0 +1,139 @@ +architecture: CascadeRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +max_iters: 90000 +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/ResNet50_cos_pretrained.tar +weights: output/cascade_rcnn_dcn_r50_fpn_1x/model_final +metric: COCO +num_classes: 81 + +CascadeRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: CascadeBBoxHead + bbox_assigner: CascadeBBoxAssigner + +ResNet: + norm_type: bn + depth: 50 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + variant: b + dcn_v2_stages: [3, 4, 5] + +FPN: + min_level: 2 + max_level: 6 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + min_level: 2 + max_level: 6 + num_chan: 256 + rpn_target_assign: + rpn_batch_size_per_im: 256 + rpn_fg_fraction: 0.5 + rpn_positive_overlap: 0.7 + rpn_negative_overlap: 0.3 + rpn_straddle_thresh: 0.0 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + min_level: 2 + max_level: 5 + box_resolution: 7 + sampling_ratio: 2 + +CascadeBBoxAssigner: + batch_size_per_im: 512 + bbox_reg_weights: [10, 20, 30] + bg_thresh_lo: [0.0, 0.0, 0.0] + bg_thresh_hi: [0.5, 0.6, 0.7] + fg_thresh: [0.5, 0.6, 0.7] + fg_fraction: 0.25 + +CascadeBBoxHead: + head: CascadeTwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +CascadeTwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.02 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [60000, 80000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 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 new file mode 100644 index 0000000000000000000000000000000000000000..dbbe2d8014d716e68ba481f4b66eb7fe50164356 --- /dev/null +++ b/configs/dcn/cascade_rcnn_dcn_x101_vd_64x4d_fpn_1x.yml @@ -0,0 +1,141 @@ +architecture: CascadeRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +max_iters: 90000 +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 +weights: output/cascade_rcnn_dcn_x101_vd_64x4d_fpn_1x/model_final +metric: COCO +num_classes: 81 + +CascadeRCNN: + backbone: ResNeXt + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: CascadeBBoxHead + bbox_assigner: CascadeBBoxAssigner + +ResNeXt: + norm_type: bn + depth: 101 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + group_width: 4 + groups: 64 + variant: d + dcn_v2_stages: [3, 4, 5] + +FPN: + min_level: 2 + max_level: 6 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + min_level: 2 + max_level: 6 + num_chan: 256 + rpn_target_assign: + rpn_batch_size_per_im: 256 + rpn_fg_fraction: 0.5 + rpn_positive_overlap: 0.7 + rpn_negative_overlap: 0.3 + rpn_straddle_thresh: 0.0 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + min_level: 2 + max_level: 5 + box_resolution: 7 + sampling_ratio: 2 + +CascadeBBoxAssigner: + batch_size_per_im: 512 + bbox_reg_weights: [10, 20, 30] + bg_thresh_lo: [0.0, 0.0, 0.0] + bg_thresh_hi: [0.5, 0.6, 0.7] + fg_thresh: [0.5, 0.6, 0.7] + fg_fraction: 0.25 + +CascadeBBoxHead: + head: CascadeTwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +CascadeTwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.02 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [60000, 80000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/dcn/faster_rcnn_dcn_r101_vd_fpn_1x.yml b/configs/dcn/faster_rcnn_dcn_r101_vd_fpn_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..d6a949efb1e68ae7c258e13f343d46e81746f77b --- /dev/null +++ b/configs/dcn/faster_rcnn_dcn_r101_vd_fpn_1x.yml @@ -0,0 +1,139 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +max_iters: 90000 +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 +weights: output/faster_rcnn_dcn_r101_vd_fpn_1x/model_final +metric: COCO +num_classes: 81 + +FasterRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + depth: 101 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: bn + variant: d + dcn_v2_stages: [3, 4, 5] + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 2000 + pre_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 1000 + pre_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + box_resolution: 7 + sampling_ratio: 2 + +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: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.02 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [60000, 80000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + factor: 0.0001 + type: L2 + +FasterRCNNTrainFeed: + # 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 new file mode 100644 index 0000000000000000000000000000000000000000..2048d61e46d4ce93ffb1c0d3bfd1075be2ea4472 --- /dev/null +++ b/configs/dcn/faster_rcnn_dcn_r50_fpn_1x.yml @@ -0,0 +1,138 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +max_iters: 90000 +use_gpu: true +snapshot_iter: 10000 +log_smooth_window: 20 +log_iter: 20 +save_dir: output +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_cos_pretrained.tar +metric: COCO +weights: output/faster_rcnn_dcn_r50_fpn_1x/model_final +num_classes: 81 + +FasterRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + depth: 50 + norm_type: bn + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + dcn_v2_stages: [3, 4, 5] + +FPN: + min_level: 2 + max_level: 6 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + min_level: 2 + max_level: 6 + num_chan: 256 + rpn_target_assign: + rpn_batch_size_per_im: 256 + rpn_fg_fraction: 0.5 + rpn_positive_overlap: 0.7 + rpn_negative_overlap: 0.3 + rpn_straddle_thresh: 0.0 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + min_level: 2 + max_level: 5 + box_resolution: 7 + sampling_ratio: 2 + +BBoxAssigner: + batch_size_per_im: 512 + bbox_reg_weights: [0.1, 0.1, 0.2, 0.2] + bg_thresh_lo: 0.0 + bg_thresh_hi: 0.5 + fg_fraction: 0.25 + fg_thresh: 0.5 + +BBoxHead: + head: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.02 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [60000, 80000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/dcn/faster_rcnn_dcn_r50_vd_fpn_2x.yml b/configs/dcn/faster_rcnn_dcn_r50_vd_fpn_2x.yml new file mode 100644 index 0000000000000000000000000000000000000000..f0cb0ba27d31247415a479619d63da265b16df27 --- /dev/null +++ b/configs/dcn/faster_rcnn_dcn_r50_vd_fpn_2x.yml @@ -0,0 +1,139 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +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/ResNet50_vd_pretrained.tar +weights: output/faster_rcnn_dcn_r50_vd_fpn_2x/model_final +metric: COCO +num_classes: 81 + +FasterRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + depth: 50 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: bn + variant: d + dcn_v2_stages: [3, 4, 5] + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 2000 + pre_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 1000 + pre_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + box_resolution: 7 + sampling_ratio: 2 + +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: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.02 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [120000, 160000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + factor: 0.0001 + type: L2 + +FasterRCNNTrainFeed: + # 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 new file mode 100644 index 0000000000000000000000000000000000000000..b6d7b81cc3cc1944e71b0159bec1bc56c35dcf53 --- /dev/null +++ b/configs/dcn/faster_rcnn_dcn_x101_vd_64x4d_fpn_1x.yml @@ -0,0 +1,143 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +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 +weights: output/faster_rcnn_dcn_x101_vd_64x4d_fpn_1x/model_final +metric: COCO +num_classes: 81 + +FasterRCNN: + backbone: ResNeXt + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNeXt: + depth: 101 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + group_width: 4 + groups: 64 + norm_type: bn + variant: d + dcn_v2_stages: [3, 4, 5] + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 2000 + pre_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 1000 + pre_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + box_resolution: 7 + sampling_ratio: 2 + +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: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [120000, 160000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/dcn/mask_rcnn_dcn_r101_vd_fpn_1x.yml b/configs/dcn/mask_rcnn_dcn_r101_vd_fpn_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..f5a0b7c458a0a0856a98533a047407f58c684adc --- /dev/null +++ b/configs/dcn/mask_rcnn_dcn_r101_vd_fpn_1x.yml @@ -0,0 +1,146 @@ +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 +weights: output/mask_rcnn_dcn_r101_vd_fpn_1x/model_final +metric: COCO +num_classes: 81 + +MaskRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + depth: 101 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: bn + variant: d + dcn_v2_stages: [3, 4, 5] + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + anchor_generator: + aspect_ratios: [0.5, 1.0, 2.0] + variance: [1.0, 1.0, 1.0, 1.0] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + sampling_ratio: 2 + box_resolution: 7 + mask_resolution: 14 + +MaskHead: + dilation: 1 + conv_dim: 256 + num_convs: 4 + resolution: 28 + +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 + +MaskAssigner: + resolution: 28 + +BBoxHead: + head: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [120000, 160000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/dcn/mask_rcnn_dcn_r50_fpn_1x.yml b/configs/dcn/mask_rcnn_dcn_r50_fpn_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..04653001e9bc27bc247fe420076bd12923199263 --- /dev/null +++ b/configs/dcn/mask_rcnn_dcn_r50_fpn_1x.yml @@ -0,0 +1,145 @@ +architecture: MaskRCNN +train_feed: MaskRCNNTrainFeed +eval_feed: MaskRCNNEvalFeed +test_feed: MaskRCNNTestFeed +use_gpu: true +max_iters: 180000 +snapshot_iter: 10000 +log_smooth_window: 20 +log_iter: 20 +save_dir: output +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_cos_pretrained.tar +metric: COCO +weights: output/mask_rcnn_dcn_r50_fpn_1x/model_final/ +num_classes: 81 + +MaskRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + depth: 50 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: bn + dcn_v2_stages: [3, 4, 5] + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + anchor_generator: + aspect_ratios: [0.5, 1.0, 2.0] + variance: [1.0, 1.0, 1.0, 1.0] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + sampling_ratio: 2 + box_resolution: 7 + mask_resolution: 14 + +MaskHead: + dilation: 1 + conv_dim: 256 + num_convs: 4 + resolution: 28 + +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 + +MaskAssigner: + resolution: 28 + +BBoxHead: + head: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [120000, 160000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/dcn/mask_rcnn_dcn_r50_vd_fpn_2x.yml b/configs/dcn/mask_rcnn_dcn_r50_vd_fpn_2x.yml new file mode 100644 index 0000000000000000000000000000000000000000..d008fb84dcd24d0853a53717000a4b6578002564 --- /dev/null +++ b/configs/dcn/mask_rcnn_dcn_r50_vd_fpn_2x.yml @@ -0,0 +1,147 @@ +architecture: MaskRCNN +train_feed: MaskRCNNTrainFeed +eval_feed: MaskRCNNEvalFeed +test_feed: MaskRCNNTestFeed +use_gpu: true +max_iters: 360000 +snapshot_iter: 10000 +log_smooth_window: 20 +log_iter: 20 +save_dir: output +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_vd_pretrained.tar +metric: COCO +weights: output/mask_rcnn_dcn_r50_vd_fpn_2x/model_final/ +num_classes: 81 + +MaskRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + depth: 50 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: bn + variant: d + dcn_v2_stages: [3, 4, 5] + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + anchor_generator: + aspect_ratios: [0.5, 1.0, 2.0] + variance: [1.0, 1.0, 1.0, 1.0] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + box_resolution: 7 + sampling_ratio: 2 + mask_resolution: 14 + +MaskHead: + dilation: 1 + conv_dim: 256 + num_convs: 4 + resolution: 28 + +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 + +MaskAssigner: + resolution: 28 + +BBoxHead: + head: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [240000, 320000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 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 new file mode 100644 index 0000000000000000000000000000000000000000..8076d1a5d4c209e174cf02d1a5dd36c3716456f9 --- /dev/null +++ b/configs/dcn/mask_rcnn_dcn_x101_vd_64x4d_fpn_1x.yml @@ -0,0 +1,148 @@ +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 +weights: output/mask_rcnn_dcn_x101_vd_64x4d_fpn_1x/model_final +metric: COCO +num_classes: 81 + +MaskRCNN: + backbone: ResNeXt + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNeXt: + depth: 101 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + group_width: 4 + groups: 64 + norm_type: bn + variant: d + dcn_v2_stages: [3, 4, 5] + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + anchor_generator: + aspect_ratios: [0.5, 1.0, 2.0] + variance: [1.0, 1.0, 1.0, 1.0] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + sampling_ratio: 2 + box_resolution: 7 + mask_resolution: 14 + +MaskHead: + dilation: 1 + conv_dim: 256 + num_convs: 4 + resolution: 28 + +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 + +MaskAssigner: + resolution: 28 + +BBoxHead: + head: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [120000, 160000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/face_detection/README.md b/configs/face_detection/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b5e2119f8148dfa3a0ed5930ca285cce50236e2e --- /dev/null +++ b/configs/face_detection/README.md @@ -0,0 +1,261 @@ +English | [简体中文](README_cn.md) + +# FaceDetection +The goal of FaceDetection is to provide efficient and high-speed face detection solutions, +including cutting-edge and classic models. + + +
+ +
+ +## Data Pipline +We use the [WIDER FACE dataset](http://shuoyang1213.me/WIDERFACE/) to carry out the training +and testing of the model, the official website gives detailed data introduction. +- WIDER Face data source: +Loads `wider_face` type dataset with directory structures like this: + + ``` + dataset/wider_face/ + ├── wider_face_split + │ ├── wider_face_train_bbx_gt.txt + │ ├── wider_face_val_bbx_gt.txt + ├── WIDER_train + │ ├── images + │ │ ├── 0--Parade + │ │ │ ├── 0_Parade_marchingband_1_100.jpg + │ │ │ ├── 0_Parade_marchingband_1_381.jpg + │ │ │ │ ... + │ │ ├── 10--People_Marching + │ │ │ ... + ├── WIDER_val + │ ├── images + │ │ ├── 0--Parade + │ │ │ ├── 0_Parade_marchingband_1_1004.jpg + │ │ │ ├── 0_Parade_marchingband_1_1045.jpg + │ │ │ │ ... + │ │ ├── 10--People_Marching + │ │ │ ... + ``` + +- Download dataset manually: +To download the WIDER FACE dataset, run the following commands: +``` +cd dataset/wider_face && ./download.sh +``` + +- Download dataset automatically: +If a training session is started but the dataset is not setup properly +(e.g, not found in dataset/wider_face), PaddleDetection can automatically +download them from [WIDER FACE dataset](http://shuoyang1213.me/WIDERFACE/), +the decompressed datasets will be cached in ~/.cache/paddle/dataset/ and can be discovered +automatically subsequently. + +### Data Augmentation + +- **Data-anchor-sampling:** Randomly transform the scale of the image to a certain range of scales, +greatly enhancing the scale change of the face. The specific operation is to obtain $v=\sqrt{width * height}$ +according to the randomly selected face height and width, and judge the value of `v` in which interval of + `[16,32,64,128]`. Assuming `v=45` && `32[1](#lite) | NAS [2](#nas) | +|:------------------------:|:--------:|:--------------------------:|:------------------------:| +| [BlazeFace](#BlazeFace) | ✓ | ✓ | ✓ | +| [FaceBoxes](#FaceBoxes) | ✓ | ✓ | x | + +[1] `Lite` edition means reduces the number of network layers and channels. +[2] `NAS` edition means use `Neural Architecture Search` algorithm to +optimized network structure. + +**Todo List:** +- [ ] HamBox +- [ ] Pyramidbox + +### Model Zoo + +#### mAP in WIDER FACE + +| Architecture | Type | Size | Img/gpu | Lr schd | Easy Set | Medium Set | Hard Set | Download | +|:------------:|:--------:|:----:|:-------:|:-------:|:---------:|:----------:|:---------:|:--------:| +| BlazeFace | Original | 640 | 8 | 32w | **0.915** | **0.892** | **0.797** | [model](https://paddlemodels.bj.bcebos.com/object_detection/blazeface_original.tar) | +| BlazeFace | Lite | 640 | 8 | 32w | 0.909 | 0.885 | 0.781 | [model](https://paddlemodels.bj.bcebos.com/object_detection/blazeface_lite.tar) | +| BlazeFace | NAS | 640 | 8 | 32w | 0.837 | 0.807 | 0.658 | [model](https://paddlemodels.bj.bcebos.com/object_detection/blazeface_nas.tar) | +| FaceBoxes | Original | 640 | 8 | 32w | 0.875 | 0.848 | 0.568 | [model](https://paddlemodels.bj.bcebos.com/object_detection/faceboxes_original.tar) | +| FaceBoxes | Lite | 640 | 8 | 32w | 0.898 | 0.872 | 0.752 | [model](https://paddlemodels.bj.bcebos.com/object_detection/faceboxes_lite.tar) | + +**NOTES:** +- Get mAP in `Easy/Medium/Hard Set` by multi-scale evaluation in `tools/face_eval.py`. +For details can refer to [Evaluation](#Evaluate-on-the-WIDER-FACE). +- BlazeFace-Lite Training and Testing ues [blazeface.yml](../../configs/face_detection/blazeface.yml) +configs file and set `lite_edition: true`. + +#### mAP in FDDB + +| Architecture | Type | Size | DistROC | ContROC | +|:------------:|:--------:|:----:|:-------:|:-------:| +| BlazeFace | Original | 640 | **0.992** | **0.762** | +| BlazeFace | Lite | 640 | 0.990 | 0.756 | +| BlazeFace | NAS | 640 | 0.981 | 0.741 | +| FaceBoxes | Original | 640 | 0.985 | 0.731 | +| FaceBoxes | Lite | 640 | 0.987 | 0.741 | + +**NOTES:** +- Get mAP by multi-scale evaluation on the FDDB dataset. +For details can refer to [Evaluation](#Evaluate-on-the-FDDB). + +#### Infer Time and Model Size comparison + +| Architecture | Type | Size | P4 (ms) | CPU (ms) | ARM (ms) | File size (MB) | Flops | +|:------------:|:--------:|:----:|:---------:|:--------:|:----------:|:--------------:|:---------:| +| BlazeFace | Original | 128 | - | - | - | - | - | +| BlazeFace | Lite | 128 | - | - | - | - | - | +| BlazeFace | NAS | 128 | - | - | - | - | - | +| FaceBoxes | Original | 128 | - | - | - | - | - | +| FaceBoxes | Lite | 128 | - | - | - | - | - | +| BlazeFace | Original | 320 | - | - | - | - | - | +| BlazeFace | Lite | 320 | - | - | - | - | - | +| BlazeFace | NAS | 320 | - | - | - | - | - | +| FaceBoxes | Original | 320 | - | - | - | - | - | +| FaceBoxes | Lite | 320 | - | - | - | - | - | +| BlazeFace | Original | 640 | - | - | - | - | - | +| BlazeFace | Lite | 640 | - | - | - | - | - | +| BlazeFace | NAS | 640 | - | - | - | - | - | +| FaceBoxes | Original | 640 | - | - | - | - | - | +| FaceBoxes | Lite | 640 | - | - | - | - | - | + + +**NOTES:** +- CPU: i5-7360U @ 2.30GHz. Single core and single thread. + + + +## Get Started +`Training` and `Inference` please refer to [GETTING_STARTED.md](../../docs/GETTING_STARTED.md) +- **NOTES:** +- `BlazeFace` and `FaceBoxes` is trained in 4 GPU with `batch_size=8` per gpu (total batch size as 32) +and trained 320000 iters.(If your GPU count is not 4, please refer to the rule of training parameters +in the table of [calculation rules](../../docs/GETTING_STARTED.md#faq)) +- Currently we do not support evaluation in training. + +### Evaluation +``` +export CUDA_VISIBLE_DEVICES=0 +export PYTHONPATH=$PYTHONPATH:. +python tools/face_eval.py -c configs/face_detection/blazeface.yml +``` +- Optional arguments +- `-d` or `--dataset_dir`: Dataset path, same as dataset_dir of configs. Such as: `-d dataset/wider_face`. +- `-f` or `--output_eval`: Evaluation file directory, default is `output/pred`. +- `-e` or `--eval_mode`: Evaluation mode, include `widerface` and `fddb`, default is `widerface`. +- `--multi_scale`: If you add this action button in the command, it will select `multi_scale` evaluation. +Default is `False`, it will select `single-scale` evaluation. + +After the evaluation is completed, the test result in txt format will be generated in `output/pred`, +and then mAP will be calculated according to different data sets. If you set `--eval_mode=widerface`, +it will [Evaluate on the WIDER FACE](#Evaluate-on-the-WIDER-FACE).If you set `--eval_mode=fddb`, +it will [Evaluate on the FDDB](#Evaluate-on-the-FDDB). + +#### Evaluate on the WIDER FACE +- Download the official evaluation script to evaluate the AP metrics: +``` +wget http://mmlab.ie.cuhk.edu.hk/projects/WIDERFace/support/eval_script/eval_tools.zip +unzip eval_tools.zip && rm -f eval_tools.zip +``` +- Modify the result path and the name of the curve to be drawn in `eval_tools/wider_eval.m`: +``` +# Modify the folder name where the result is stored. +pred_dir = './pred'; +# Modify the name of the curve to be drawn +legend_name = 'Fluid-BlazeFace'; +``` +- `wider_eval.m` is the main execution program of the evaluation module. The run command is as follows: +``` +matlab -nodesktop -nosplash -nojvm -r "run wider_eval.m;quit;" +``` + +#### Evaluate on the FDDB +[FDDB dataset](http://vis-www.cs.umass.edu/fddb/) details can refer to FDDB's official website. +- Download the official dataset and evaluation script to evaluate the ROC metrics: +``` +#external link to the Faces in the Wild data set +wget http://tamaraberg.com/faceDataset/originalPics.tar.gz +#The annotations are split into ten folds. See README for details. +wget http://vis-www.cs.umass.edu/fddb/FDDB-folds.tgz +#information on directory structure and file formats +wget http://vis-www.cs.umass.edu/fddb/README.txt +``` +- Install OpenCV: Requires [OpenCV library](http://sourceforge.net/projects/opencvlibrary/) +If the utility 'pkg-config' is not available for your operating system, +edit the Makefile to manually specify the OpenCV flags as following: +``` +INCS = -I/usr/local/include/opencv +LIBS = -L/usr/local/lib -lcxcore -lcv -lhighgui -lcvaux -lml +``` + +- Compile FDDB evaluation code: execute `make` in evaluation folder. + +- Generate full image path list and groundtruth in FDDB-folds. The run command is as follows: +``` +cat `ls|grep -v"ellipse"` > filePath.txt` and `cat *ellipse* > fddb_annotFile.txt` +``` +- Evaluation +Finally evaluation command is: +``` +./evaluate -a ./FDDB/FDDB-folds/fddb_annotFile.txt \ + -d DETECTION_RESULT.txt -f 0 \ + -i ./FDDB -l ./FDDB/FDDB-folds/filePath.txt \ + -r ./OUTPUT_DIR -z .jpg +``` +**NOTES:** The interpretation of the argument can be performed by `./evaluate --help`. + +## Algorithm Description + +### BlazeFace +**Introduction:** +[BlazeFace](https://arxiv.org/abs/1907.05047) is Google Research published face detection model. +It's lightweight but good performance, and tailored for mobile GPU inference. It runs at a speed +of 200-1000+ FPS on flagship devices. + +**Particularity:** +- Anchor scheme stops at 8×8(input 128x128), 6 anchors per pixel at that resolution. +- 5 single, and 6 double BlazeBlocks: 5×5 depthwise convs, same accuracy with fewer layers. +- Replace the non-maximum suppression algorithm with a blending strategy that estimates the +regression parameters of a bounding box as a weighted mean between the overlapping predictions. + +**Edition information:** +- Original: Reference original paper reproduction. +- Lite: Replace 5x5 conv with 3x3 conv, fewer network layers and conv channels. +- NAS: use `Neural Architecture Search` algorithm to optimized network structure, +less network layer and conv channel number than `Lite`. + +### FaceBoxes +**Introduction:** +[FaceBoxes](https://arxiv.org/abs/1708.05234) which named A CPU Real-time Face Detector +with High Accuracy is face detector proposed by Shifeng Zhang, with high performance on +both speed and accuracy. This paper is published by IJCB(2017). + +**Particularity:** +- Anchor scheme stops at 20x20, 10x10, 5x5, which network input size is 640x640, +including 3, 1, 1 anchors per pixel at each resolution. The corresponding densities +are 1, 2, 4(20x20), 4(10x10) and 4(5x5). +- 2 convs with CReLU, 2 poolings, 3 inceptions and 2 convs with ReLU. +- Use density prior box to improve detection accuracy. + +**Edition information:** +- Original: Reference original paper reproduction. +- Lite: 2 convs with CReLU, 1 pooling, 2 convs with ReLU, 3 inceptions and 2 convs with ReLU. +Anchor scheme stops at 80x80 and 40x40, including 3, 1 anchors per pixel at each resolution. +The corresponding densities are 1, 2, 4(80x80) and 4(40x40), using less conv channel number than lite. + + +## Contributing +Contributions are highly welcomed and we would really appreciate your feedback!! diff --git a/configs/face_detection/blazeface.yml b/configs/face_detection/blazeface.yml new file mode 100644 index 0000000000000000000000000000000000000000..692f14a7cc8091bc8df1f5edbfbca2a9c59b0073 --- /dev/null +++ b/configs/face_detection/blazeface.yml @@ -0,0 +1,130 @@ +architecture: BlazeFace +max_iters: 320000 +train_feed: SSDTrainFeed +eval_feed: SSDEvalFeed +test_feed: SSDTestFeed +pretrain_weights: +use_gpu: true +snapshot_iter: 10000 +log_smooth_window: 20 +log_iter: 20 +metric: WIDERFACE +save_dir: output +weights: output/blazeface/model_final/ +# 1(label_class) + 1(background) +num_classes: 2 + +BlazeFace: + backbone: BlazeNet + output_decoder: + keep_top_k: 750 + nms_threshold: 0.3 + nms_top_k: 5000 + score_threshold: 0.01 + min_sizes: [[16.,24.], [32., 48., 64., 80., 96., 128.]] + use_density_prior_box: false + +BlazeNet: + with_extra_blocks: true + lite_edition: false + +LearningRate: + base_lr: 0.001 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [240000, 300000] + +OptimizerBuilder: + optimizer: + momentum: 0.0 + type: RMSPropOptimizer + regularizer: + factor: 0.0005 + type: L2 + +SSDTrainFeed: + batch_size: 8 + use_process: True + dataset: + dataset_dir: dataset/wider_face + annotation: 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 + brightness_upper: 1.125 + is_order: true + - !ExpandImage + max_ratio: 4 + prob: 0.5 + - !CropImageWithDataAchorSampling + anchor_sampler: + - [1, 10, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.2, 0.0] + batch_sampler: + - [1, 50, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + - [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + - [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + - [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + - [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + target_size: 640 + - !RandomInterpImage + target_size: 640 + - !RandomFlipImage + is_normalized: true + - !Permute {} + - !NormalizeImage + is_scale: false + mean: [104, 117, 123] + std: [127.502231, 127.502231, 127.502231] + +SSDEvalFeed: + batch_size: 1 + use_process: false + fields: ['image', 'im_id', 'gt_box'] + dataset: + dataset_dir: dataset/wider_face + annotation: 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 + dataset: + 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] diff --git a/configs/face_detection/blazeface_nas.yml b/configs/face_detection/blazeface_nas.yml new file mode 100644 index 0000000000000000000000000000000000000000..45356bda7998c18b286edaa4e308f21875cce9d3 --- /dev/null +++ b/configs/face_detection/blazeface_nas.yml @@ -0,0 +1,132 @@ +architecture: BlazeFace +max_iters: 320000 +train_feed: SSDTrainFeed +eval_feed: SSDEvalFeed +test_feed: SSDTestFeed +pretrain_weights: +use_gpu: true +snapshot_iter: 10000 +log_smooth_window: 20 +log_iter: 20 +metric: WIDERFACE +save_dir: output +weights: output/blazeface_nas/model_final/ +# 1(label_class) + 1(background) +num_classes: 2 + +BlazeFace: + backbone: BlazeNet + output_decoder: + keep_top_k: 750 + nms_threshold: 0.3 + nms_top_k: 5000 + score_threshold: 0.01 + min_sizes: [[16.,24.], [32., 48., 64., 80., 96., 128.]] + use_density_prior_box: false + +BlazeNet: + blaze_filters: [[12, 12], [12, 12, 2], [12, 12]] + double_blaze_filters: [[12, 16, 24, 2], [24, 12, 24], [24, 16, 72, 2], [72, 12, 72]] + with_extra_blocks: true + lite_edition: false + +LearningRate: + base_lr: 0.001 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [240000, 300000] + +OptimizerBuilder: + optimizer: + momentum: 0.0 + type: RMSPropOptimizer + regularizer: + factor: 0.0005 + type: L2 + +SSDTrainFeed: + batch_size: 8 + use_process: True + dataset: + dataset_dir: dataset/wider_face + annotation: 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 + brightness_upper: 1.125 + is_order: true + - !ExpandImage + max_ratio: 4 + prob: 0.5 + - !CropImageWithDataAchorSampling + anchor_sampler: + - [1, 10, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.2, 0.0] + batch_sampler: + - [1, 50, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + - [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + - [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + - [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + - [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + target_size: 640 + - !RandomInterpImage + target_size: 640 + - !RandomFlipImage + is_normalized: true + - !Permute {} + - !NormalizeImage + is_scale: false + mean: [104, 117, 123] + std: [127.502231, 127.502231, 127.502231] + +SSDEvalFeed: + batch_size: 1 + use_process: false + fields: ['image', 'im_id', 'gt_box'] + dataset: + dataset_dir: dataset/wider_face + annotation: 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 + dataset: + 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] diff --git a/configs/face_detection/faceboxes.yml b/configs/face_detection/faceboxes.yml new file mode 100644 index 0000000000000000000000000000000000000000..b278723292ddf51e7a93a88b59f3b757f5d4455e --- /dev/null +++ b/configs/face_detection/faceboxes.yml @@ -0,0 +1,130 @@ +architecture: FaceBoxes +train_feed: SSDTrainFeed +eval_feed: SSDEvalFeed +test_feed: SSDTestFeed +pretrain_weights: +use_gpu: true +max_iters: 320000 +snapshot_iter: 10000 +log_smooth_window: 20 +log_iter: 20 +metric: WIDERFACE +save_dir: output +weights: output/faceboxes/model_final/ +# 1(label_class) + 1(background) +num_classes: 2 + +FaceBoxes: + backbone: FaceBoxNet + densities: [[4, 2, 1], [1], [1]] + fixed_sizes: [[32., 64., 128.], [256.], [512.]] + output_decoder: + keep_top_k: 750 + nms_threshold: 0.3 + nms_top_k: 5000 + score_threshold: 0.01 + +FaceBoxNet: + with_extra_blocks: true + lite_edition: false + +LearningRate: + base_lr: 0.001 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [240000, 300000] + +OptimizerBuilder: + optimizer: + momentum: 0.0 + type: RMSPropOptimizer + regularizer: + factor: 0.0005 + type: L2 + +SSDTrainFeed: + batch_size: 8 + use_process: True + dataset: + dataset_dir: dataset/wider_face + annotation: 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 + brightness_upper: 1.125 + is_order: true + - !ExpandImage + max_ratio: 4 + prob: 0.5 + - !CropImageWithDataAchorSampling + anchor_sampler: + - [1, 10, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.2, 0.0] + batch_sampler: + - [1, 50, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + - [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + - [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + - [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + - [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + target_size: 640 + - !RandomInterpImage + target_size: 640 + - !RandomFlipImage + is_normalized: true + - !Permute {} + - !NormalizeImage + is_scale: false + mean: [104, 117, 123] + std: [127.502231, 127.502231, 127.502231] + +SSDEvalFeed: + batch_size: 1 + use_process: false + fields: ['image', 'im_id', 'gt_box'] + dataset: + dataset_dir: dataset/wider_face + annotation: 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 + dataset: + 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] diff --git a/configs/face_detection/faceboxes_lite.yml b/configs/face_detection/faceboxes_lite.yml new file mode 100644 index 0000000000000000000000000000000000000000..157f0337e4fbda281b3c2fe9cdfd85dd81b51b40 --- /dev/null +++ b/configs/face_detection/faceboxes_lite.yml @@ -0,0 +1,130 @@ +architecture: FaceBoxes +train_feed: SSDTrainFeed +eval_feed: SSDEvalFeed +test_feed: SSDTestFeed +pretrain_weights: +use_gpu: true +max_iters: 320000 +snapshot_iter: 10000 +log_smooth_window: 20 +log_iter: 20 +metric: WIDERFACE +save_dir: output +weights: output/faceboxes_lite/model_final/ +# 1(label_class) + 1(background) +num_classes: 2 + +FaceBoxes: + backbone: FaceBoxNet + densities: [[2, 1, 1], [1, 1]] + fixed_sizes: [[16., 32., 64.], [96., 128.]] + output_decoder: + keep_top_k: 750 + nms_threshold: 0.3 + nms_top_k: 5000 + score_threshold: 0.01 + +FaceBoxNet: + with_extra_blocks: true + lite_edition: true + +LearningRate: + base_lr: 0.001 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [240000, 300000] + +OptimizerBuilder: + optimizer: + momentum: 0.0 + type: RMSPropOptimizer + regularizer: + factor: 0.0005 + type: L2 + +SSDTrainFeed: + batch_size: 8 + use_process: True + dataset: + dataset_dir: dataset/wider_face + annotation: 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 + brightness_upper: 1.125 + is_order: true + - !ExpandImage + max_ratio: 4 + prob: 0.5 + - !CropImageWithDataAchorSampling + anchor_sampler: + - [1, 10, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.2, 0.0] + batch_sampler: + - [1, 50, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + - [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + - [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + - [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + - [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0] + target_size: 640 + - !RandomInterpImage + target_size: 640 + - !RandomFlipImage + is_normalized: true + - !Permute {} + - !NormalizeImage + is_scale: false + mean: [104, 117, 123] + std: [127.502231, 127.502231, 127.502231] + +SSDEvalFeed: + batch_size: 1 + use_process: false + fields: ['image', 'im_id', 'gt_box'] + dataset: + dataset_dir: dataset/wider_face + annotation: 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 + dataset: + 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] diff --git a/configs/faster_rcnn_r101_1x.yml b/configs/faster_rcnn_r101_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..c72c34d4da8ce429932069d5084f4e710ddba11b --- /dev/null +++ b/configs/faster_rcnn_r101_1x.yml @@ -0,0 +1,115 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +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/ResNet101_pretrained.tar +metric: COCO +weights: output/faster_rcnn_r101_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: 101 + feature_maps: 4 + freeze_at: 2 + +ResNetC5: + depth: 101 + 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 + +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 diff --git a/configs/faster_rcnn_r101_fpn_1x.yml b/configs/faster_rcnn_r101_fpn_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..c11d6f2141a9b15c6a3ef2c50055fbc753338d53 --- /dev/null +++ b/configs/faster_rcnn_r101_fpn_1x.yml @@ -0,0 +1,136 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +max_iters: 180000 +snapshot_iter: 10000 +use_gpu: true +log_smooth_window: 20 +save_dir: output +pretrain_weights: http://paddle-imagenet-models-name.bj.bcebos.com/ResNet101_pretrained.tar +weights: output/faster_rcnn_r101_fpn_1x/model_final +metric: COCO +num_classes: 81 + +FasterRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + depth: 101 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: affine_channel + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 2000 + pre_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 1000 + pre_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + box_resolution: 7 + sampling_ratio: 2 + +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: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +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 + +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 diff --git a/configs/faster_rcnn_r101_fpn_2x.yml b/configs/faster_rcnn_r101_fpn_2x.yml new file mode 100644 index 0000000000000000000000000000000000000000..29838c78b60bddb7a92193088354fe0956e2d14b --- /dev/null +++ b/configs/faster_rcnn_r101_fpn_2x.yml @@ -0,0 +1,136 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +max_iters: 360000 +snapshot_iter: 10000 +use_gpu: true +log_smooth_window: 20 +save_dir: output +pretrain_weights: http://paddle-imagenet-models-name.bj.bcebos.com/ResNet101_pretrained.tar +weights: output/faster_rcnn_r101_fpn_2x/model_final +metric: COCO +num_classes: 81 + +FasterRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + depth: 101 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: affine_channel + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 2000 + pre_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 1000 + pre_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + box_resolution: 7 + sampling_ratio: 2 + +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: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [240000, 320000] + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/faster_rcnn_r101_vd_fpn_1x.yml b/configs/faster_rcnn_r101_vd_fpn_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..2ef717ffc846d325a6e3f3c9b78752250d692f9d --- /dev/null +++ b/configs/faster_rcnn_r101_vd_fpn_1x.yml @@ -0,0 +1,137 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +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 +weights: output/faster_rcnn_r101_vd_fpn_1x/model_final +metric: COCO +num_classes: 81 + +FasterRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + depth: 101 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: affine_channel + variant: d + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 2000 + pre_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 1000 + pre_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + box_resolution: 7 + sampling_ratio: 2 + +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: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [120000, 160000] + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/faster_rcnn_r101_vd_fpn_2x.yml b/configs/faster_rcnn_r101_vd_fpn_2x.yml new file mode 100644 index 0000000000000000000000000000000000000000..763d447c8cfb59c6fcd3045b0e8a34b8da38e73e --- /dev/null +++ b/configs/faster_rcnn_r101_vd_fpn_2x.yml @@ -0,0 +1,137 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +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/ResNet101_vd_pretrained.tar +weights: output/faster_rcnn_r101_vd_fpn_2x/model_final +metric: COCO +num_classes: 81 + +FasterRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + depth: 101 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: affine_channel + variant: d + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 2000 + pre_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 1000 + pre_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + box_resolution: 7 + sampling_ratio: 2 + +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: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [240000, 320000] + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/faster_rcnn_r50_1x.yml b/configs/faster_rcnn_r50_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..12d349612b3464cff0b945535a8d81a53b434a98 --- /dev/null +++ b/configs/faster_rcnn_r50_1x.yml @@ -0,0 +1,115 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +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 + +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 diff --git a/configs/faster_rcnn_r50_2x.yml b/configs/faster_rcnn_r50_2x.yml new file mode 100644 index 0000000000000000000000000000000000000000..255cd02663845235beac103dad7faaaf03bb90f2 --- /dev/null +++ b/configs/faster_rcnn_r50_2x.yml @@ -0,0 +1,115 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +use_gpu: true +max_iters: 360000 +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_2x/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: [240000, 320000] + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/faster_rcnn_r50_fpn_1x.yml b/configs/faster_rcnn_r50_fpn_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..c719106104f1424008db3a079e2e1ac7a3d742b9 --- /dev/null +++ b/configs/faster_rcnn_r50_fpn_1x.yml @@ -0,0 +1,137 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +max_iters: 90000 +use_gpu: true +snapshot_iter: 10000 +log_smooth_window: 20 +save_dir: output +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_cos_pretrained.tar +metric: COCO +weights: output/faster_rcnn_r50_fpn_1x/model_final +num_classes: 81 + +FasterRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + norm_type: bn + norm_decay: 0. + depth: 50 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + +FPN: + min_level: 2 + max_level: 6 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + min_level: 2 + max_level: 6 + num_chan: 256 + rpn_target_assign: + rpn_batch_size_per_im: 256 + rpn_fg_fraction: 0.5 + rpn_positive_overlap: 0.7 + rpn_negative_overlap: 0.3 + rpn_straddle_thresh: 0.0 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + min_level: 2 + max_level: 5 + box_resolution: 7 + sampling_ratio: 2 + +BBoxAssigner: + batch_size_per_im: 512 + bbox_reg_weights: [0.1, 0.1, 0.2, 0.2] + bg_thresh_lo: 0.0 + bg_thresh_hi: 0.5 + fg_fraction: 0.25 + fg_thresh: 0.5 + +BBoxHead: + head: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.02 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [60000, 80000] + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/faster_rcnn_r50_fpn_2x.yml b/configs/faster_rcnn_r50_fpn_2x.yml new file mode 100644 index 0000000000000000000000000000000000000000..12ae624f6c642ff439a21a90a4f52d1da046c164 --- /dev/null +++ b/configs/faster_rcnn_r50_fpn_2x.yml @@ -0,0 +1,137 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +max_iters: 180000 +use_gpu: true +snapshot_iter: 10000 +log_smooth_window: 20 +save_dir: output +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_cos_pretrained.tar +metric: COCO +weights: output/faster_rcnn_r50_fpn_2x/model_final +num_classes: 81 + +FasterRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + norm_type: affine_channel + norm_decay: 0. + depth: 50 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + +FPN: + min_level: 2 + max_level: 6 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + min_level: 2 + max_level: 6 + num_chan: 256 + rpn_target_assign: + rpn_batch_size_per_im: 256 + rpn_fg_fraction: 0.5 + rpn_positive_overlap: 0.7 + rpn_negative_overlap: 0.3 + rpn_straddle_thresh: 0.0 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + min_level: 2 + max_level: 5 + box_resolution: 7 + sampling_ratio: 2 + +BBoxAssigner: + batch_size_per_im: 512 + bbox_reg_weights: [0.1, 0.1, 0.2, 0.2] + bg_thresh_lo: 0.0 + bg_thresh_hi: 0.5 + fg_fraction: 0.25 + fg_thresh: 0.5 + +BBoxHead: + head: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.02 + 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 + +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/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 new file mode 100644 index 0000000000000000000000000000000000000000..f39a144a431f5998a8178c41c10ade796d270cb6 --- /dev/null +++ b/configs/faster_rcnn_r50_vd_1x.yml @@ -0,0 +1,117 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +use_gpu: true +max_iters: 180000 +log_smooth_window: 20 +save_dir: output/faster-r50-vd-c4-1x +snapshot_iter: 10000 +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_vd_pretrained.tar +metric: COCO +weights: output/faster_rcnn_r50_vd_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 + variant: d + +ResNetC5: + depth: 50 + norm_type: affine_channel + variant: d + +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.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/faster_rcnn_r50_vd_fpn_2x.yml b/configs/faster_rcnn_r50_vd_fpn_2x.yml new file mode 100644 index 0000000000000000000000000000000000000000..4b944ef9398b3dfebdc4f3731b8ef2522d103e22 --- /dev/null +++ b/configs/faster_rcnn_r50_vd_fpn_2x.yml @@ -0,0 +1,137 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +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/ResNet50_vd_pretrained.tar +weights: output/faster_rcnn_r50_vd_fpn_2x/model_final +metric: COCO +num_classes: 81 + +FasterRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + depth: 50 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: affine_channel + variant: d + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 2000 + pre_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 1000 + pre_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + box_resolution: 7 + sampling_ratio: 2 + +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: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.02 + 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 + +FasterRCNNTrainFeed: + # 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_se154_vd_fpn_s1x.yml b/configs/faster_rcnn_se154_vd_fpn_s1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..c3dd761e6637f568c32d30af2300d87fe5f600ac --- /dev/null +++ b/configs/faster_rcnn_se154_vd_fpn_s1x.yml @@ -0,0 +1,139 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +max_iters: 260000 +snapshot_iter: 10000 +use_gpu: true +log_smooth_window: 20 +save_dir: output +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/SENet154_vd_pretrained.tar +weights: output/faster_rcnn_se154_vd_fpn_s1x/model_final +metric: COCO +num_classes: 81 + +FasterRCNN: + backbone: SENet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +SENet: + depth: 152 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + group_width: 4 + groups: 64 + norm_type: affine_channel + variant: d + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 2000 + pre_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 1000 + pre_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + box_resolution: 7 + sampling_ratio: 2 + +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: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [200000, 240000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/faster_rcnn_x101_vd_64x4d_fpn_1x.yml b/configs/faster_rcnn_x101_vd_64x4d_fpn_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..adb607b6e022f3c7c66c121922e2d28e4ba3e1d0 --- /dev/null +++ b/configs/faster_rcnn_x101_vd_64x4d_fpn_1x.yml @@ -0,0 +1,142 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +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 +weights: output/faster_rcnn_x101_vd_64x4d_fpn_1x/model_final +metric: COCO +num_classes: 81 + +FasterRCNN: + backbone: ResNeXt + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNeXt: + depth: 101 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + group_width: 4 + groups: 64 + norm_type: affine_channel + variant: d + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 2000 + pre_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 1000 + pre_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + box_resolution: 7 + sampling_ratio: 2 + +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: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [120000, 160000] + values: null + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/faster_rcnn_x101_vd_64x4d_fpn_2x.yml b/configs/faster_rcnn_x101_vd_64x4d_fpn_2x.yml new file mode 100644 index 0000000000000000000000000000000000000000..ee36efbe859ab42d7391d2867bf34d45b20b340f --- /dev/null +++ b/configs/faster_rcnn_x101_vd_64x4d_fpn_2x.yml @@ -0,0 +1,141 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +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 +weights: output/faster_rcnn_x101_vd_64x4d_fpn_1x/model_final +metric: COCO +num_classes: 81 + +FasterRCNN: + backbone: ResNeXt + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNeXt: + depth: 101 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + group_width: 4 + groups: 64 + norm_type: affine_channel + variant: d + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + 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] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 2000 + pre_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 1000 + pre_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + box_resolution: 7 + sampling_ratio: 2 + +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: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [240000, 320000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/gn/cascade_mask_rcnn_r50_fpn_gn_2x.yml b/configs/gn/cascade_mask_rcnn_r50_fpn_gn_2x.yml new file mode 100644 index 0000000000000000000000000000000000000000..52c61ad4b57bf1464fe9e2816cec710563a9d707 --- /dev/null +++ b/configs/gn/cascade_mask_rcnn_r50_fpn_gn_2x.yml @@ -0,0 +1,147 @@ +architecture: CascadeMaskRCNN +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/ResNet50_cos_pretrained.tar +weights: output/cascade_mask_rcnn_r50_fpn_gn_2x/model_final/ +metric: COCO +num_classes: 81 + +CascadeMaskRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: CascadeBBoxHead + bbox_assigner: CascadeBBoxAssigner + mask_head: MaskHead + mask_assigner: MaskAssigner + +ResNet: + depth: 50 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: affine_channel + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + norm_type: gn + +FPNRPNHead: + anchor_generator: + aspect_ratios: [0.5, 1.0, 2.0] + variance: [1.0, 1.0, 1.0, 1.0] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + sampling_ratio: 2 + box_resolution: 7 + mask_resolution: 14 + +MaskHead: + dilation: 1 + conv_dim: 256 + num_convs: 4 + resolution: 28 + norm_type: gn + +CascadeBBoxAssigner: + batch_size_per_im: 512 + bbox_reg_weights: [10, 20, 30] + bg_thresh_hi: [0.5, 0.6, 0.7] + bg_thresh_lo: [0.0, 0.0, 0.0] + fg_fraction: 0.25 + fg_thresh: [0.5, 0.6, 0.7] + +MaskAssigner: + resolution: 28 + +CascadeBBoxHead: + head: CascadeXConvNormHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +CascadeXConvNormHead: + norm_type: gn + +LearningRate: + base_lr: 0.02 + 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 + +MaskRCNNTrainFeed: + 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 new file mode 100644 index 0000000000000000000000000000000000000000..a86deb4f6efab685cce14a329bd3b79f14ba36d8 --- /dev/null +++ b/configs/gn/faster_rcnn_r50_fpn_gn_2x.yml @@ -0,0 +1,137 @@ +architecture: FasterRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +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/ResNet50_cos_pretrained.tar +metric: COCO +weights: output/faster_rcnn_r50_fpn_gn/model_final +num_classes: 81 + +FasterRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + depth: 50 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: affine_channel + +FPN: + min_level: 2 + max_level: 6 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + norm_type: gn + +FPNRPNHead: + 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] + anchor_start_size: 32 + min_level: 2 + max_level: 6 + num_chan: 256 + rpn_target_assign: + rpn_batch_size_per_im: 256 + rpn_fg_fraction: 0.5 + rpn_positive_overlap: 0.7 + rpn_negative_overlap: 0.3 + rpn_straddle_thresh: 0.0 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + min_level: 2 + max_level: 5 + box_resolution: 7 + sampling_ratio: 2 + +BBoxAssigner: + batch_size_per_im: 512 + bbox_reg_weights: [0.1, 0.1, 0.2, 0.2] + bg_thresh_lo: 0.0 + bg_thresh_hi: 0.5 + fg_fraction: 0.25 + fg_thresh: 0.5 + +BBoxHead: + head: XConvNormHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +XConvNormHead: + norm_type: gn + +LearningRate: + base_lr: 0.02 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [120000, 160000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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: 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 new file mode 100644 index 0000000000000000000000000000000000000000..bffe3ba45a78d5ddcbeb20fa15a85ebd9f034532 --- /dev/null +++ b/configs/gn/mask_rcnn_r50_fpn_gn_2x.yml @@ -0,0 +1,145 @@ +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/ResNet50_cos_pretrained.tar +weights: output/mask_rcnn_r50_fpn_gn_2x/model_final/ +metric: COCO +num_classes: 81 + +MaskRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + depth: 50 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: affine_channel + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + norm_type: gn + +FPNRPNHead: + anchor_generator: + aspect_ratios: [0.5, 1.0, 2.0] + variance: [1.0, 1.0, 1.0, 1.0] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + sampling_ratio: 2 + box_resolution: 7 + mask_resolution: 14 + +MaskHead: + dilation: 1 + conv_dim: 256 + num_convs: 4 + resolution: 28 + norm_type: gn + +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 + +MaskAssigner: + resolution: 28 + +BBoxHead: + head: XConvNormHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +XConvNormHead: + norm_type: gn + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [240000, 320000] + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/mask_rcnn_r101_fpn_1x.yml b/configs/mask_rcnn_r101_fpn_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..12229a074fbcf1549a3d7581fcd91bd6c124d516 --- /dev/null +++ b/configs/mask_rcnn_r101_fpn_1x.yml @@ -0,0 +1,143 @@ +architecture: MaskRCNN +train_feed: MaskRCNNTrainFeed +eval_feed: MaskRCNNEvalFeed +test_feed: MaskRCNNTestFeed +use_gpu: true +max_iters: 180000 +snapshot_iter: 10000 +log_smooth_window: 20 +save_dir: output +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet101_pretrained.tar +metric: COCO +weights: output/mask_rcnn_r101_fpn_1x/model_final/ +num_classes: 81 + +MaskRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + depth: 101 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: affine_channel + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + anchor_generator: + aspect_ratios: [0.5, 1.0, 2.0] + variance: [1.0, 1.0, 1.0, 1.0] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + sampling_ratio: 2 + box_resolution: 7 + mask_resolution: 14 + +MaskHead: + dilation: 1 + conv_dim: 256 + num_convs: 4 + resolution: 28 + +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 + +MaskAssigner: + resolution: 28 + +BBoxHead: + head: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +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 + +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 diff --git a/configs/mask_rcnn_r101_vd_fpn_1x.yml b/configs/mask_rcnn_r101_vd_fpn_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..91bb40c2f0d920ca1961e93a5e7adf804ff28d1b --- /dev/null +++ b/configs/mask_rcnn_r101_vd_fpn_1x.yml @@ -0,0 +1,144 @@ +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 +weights: output/mask_rcnn_r101_vd_fpn_1x/model_final +metric: COCO +num_classes: 81 + +MaskRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + depth: 101 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: affine_channel + variant: d + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + anchor_generator: + aspect_ratios: [0.5, 1.0, 2.0] + variance: [1.0, 1.0, 1.0, 1.0] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + sampling_ratio: 2 + box_resolution: 7 + mask_resolution: 14 + +MaskHead: + dilation: 1 + conv_dim: 256 + num_convs: 4 + resolution: 28 + +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 + +MaskAssigner: + resolution: 28 + +BBoxHead: + head: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [120000, 160000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/mask_rcnn_r50_1x.yml b/configs/mask_rcnn_r50_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..6c3dd8418e55c899c1ea0e0bc7db1d39d9a0be9d --- /dev/null +++ b/configs/mask_rcnn_r50_1x.yml @@ -0,0 +1,123 @@ +architecture: MaskRCNN +train_feed: MaskRCNNTrainFeed +eval_feed: MaskRCNNEvalFeed +test_feed: MaskRCNNTestFeed +use_gpu: true +max_iters: 180000 +snapshot_iter: 10000 +log_smooth_window: 20 +save_dir: output +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_cos_pretrained.tar +metric: COCO +weights: output/mask_rcnn_r50_1x/model_final +num_classes: 81 + +MaskRCNN: + backbone: ResNet + rpn_head: RPNHead + roi_extractor: RoIAlign + bbox_assigner: BBoxAssigner + bbox_head: BBoxHead + mask_assigner: MaskAssigner + mask_head: MaskHead + +ResNet: + norm_type: affine_channel + norm_decay: 0. + 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 + 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 + spatial_scale: 0.0625 + sampling_ratio: 0 + +BBoxHead: + head: ResNetC5 + nms: + keep_top_k: 100 + nms_threshold: 0.5 + normalized: false + score_threshold: 0.05 + +MaskHead: + dilation: 1 + conv_dim: 256 + resolution: 14 + +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 + +MaskAssigner: + resolution: 14 + +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 + +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 diff --git a/configs/mask_rcnn_r50_2x.yml b/configs/mask_rcnn_r50_2x.yml new file mode 100644 index 0000000000000000000000000000000000000000..091b0cf89bfe44e74ac807bfb0f1a7a8ea1a6454 --- /dev/null +++ b/configs/mask_rcnn_r50_2x.yml @@ -0,0 +1,125 @@ +architecture: MaskRCNN +train_feed: MaskRCNNTrainFeed +eval_feed: MaskRCNNEvalFeed +test_feed: MaskRCNNTestFeed +use_gpu: true +max_iters: 360000 +snapshot_iter: 10000 +log_smooth_window: 20 +save_dir: output +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_cos_pretrained.tar +metric: COCO +weights: output/mask_rcnn_r50_2x/model_final/ +num_classes: 81 + +MaskRCNN: + backbone: ResNet + rpn_head: RPNHead + roi_extractor: RoIAlign + bbox_assigner: BBoxAssigner + bbox_head: BBoxHead + mask_assigner: MaskAssigner + mask_head: MaskHead + + +ResNet: + norm_type: affine_channel + norm_decay: 0. + 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 + 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 + spatial_scale: 0.0625 + sampling_ratio: 0 + +BBoxHead: + head: ResNetC5 + nms: + keep_top_k: 100 + nms_threshold: 0.5 + normalized: false + score_threshold: 0.05 + +MaskHead: + dilation: 1 + conv_dim: 256 + resolution: 14 + +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 + +MaskAssigner: + resolution: 14 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [240000, 320000] + #start the warm up from base_lr * start_factor + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/mask_rcnn_r50_fpn_1x.yml b/configs/mask_rcnn_r50_fpn_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..a889ea283f445974e11d38479162b388d69ec3ad --- /dev/null +++ b/configs/mask_rcnn_r50_fpn_1x.yml @@ -0,0 +1,143 @@ +architecture: MaskRCNN +train_feed: MaskRCNNTrainFeed +eval_feed: MaskRCNNEvalFeed +test_feed: MaskRCNNTestFeed +use_gpu: true +max_iters: 180000 +snapshot_iter: 10000 +log_smooth_window: 20 +save_dir: output +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_cos_pretrained.tar +metric: COCO +weights: output/mask_rcnn_r50_fpn_1x/model_final/ +num_classes: 81 + +MaskRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + depth: 50 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: bn + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + anchor_generator: + aspect_ratios: [0.5, 1.0, 2.0] + variance: [1.0, 1.0, 1.0, 1.0] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + sampling_ratio: 2 + box_resolution: 7 + mask_resolution: 14 + +MaskHead: + dilation: 1 + conv_dim: 256 + num_convs: 4 + resolution: 28 + +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 + +MaskAssigner: + resolution: 28 + +BBoxHead: + head: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +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 + +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 diff --git a/configs/mask_rcnn_r50_fpn_2x.yml b/configs/mask_rcnn_r50_fpn_2x.yml new file mode 100644 index 0000000000000000000000000000000000000000..08977bba39b688920f427442c01510f93efa412b --- /dev/null +++ b/configs/mask_rcnn_r50_fpn_2x.yml @@ -0,0 +1,143 @@ +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/ResNet50_cos_pretrained.tar +weights: output/mask_rcnn_r50_fpn_2x/model_final/ +metric: COCO +num_classes: 81 + +MaskRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + depth: 50 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: affine_channel + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + anchor_generator: + aspect_ratios: [0.5, 1.0, 2.0] + variance: [1.0, 1.0, 1.0, 1.0] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + sampling_ratio: 2 + box_resolution: 7 + mask_resolution: 14 + +MaskHead: + dilation: 1 + conv_dim: 256 + num_convs: 4 + resolution: 28 + +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 + +MaskAssigner: + resolution: 28 + +BBoxHead: + head: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [240000, 320000] + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/mask_rcnn_r50_vd_fpn_2x.yml b/configs/mask_rcnn_r50_vd_fpn_2x.yml new file mode 100644 index 0000000000000000000000000000000000000000..12a5057528eaada329d70058acf3aa9a13727237 --- /dev/null +++ b/configs/mask_rcnn_r50_vd_fpn_2x.yml @@ -0,0 +1,145 @@ +architecture: MaskRCNN +train_feed: MaskRCNNTrainFeed +eval_feed: MaskRCNNEvalFeed +test_feed: MaskRCNNTestFeed +use_gpu: true +max_iters: 360000 +snapshot_iter: 10000 +log_smooth_window: 20 +save_dir: output +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_vd_pretrained.tar +metric: COCO +weights: output/mask_rcnn_r50_vd_fpn_2x/model_final/ +num_classes: 81 + +MaskRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNet: + depth: 50 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + norm_type: affine_channel + variant: d + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + anchor_generator: + aspect_ratios: [0.5, 1.0, 2.0] + variance: [1.0, 1.0, 1.0, 1.0] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + box_resolution: 7 + sampling_ratio: 2 + mask_resolution: 14 + +MaskHead: + dilation: 1 + conv_dim: 256 + num_convs: 4 + resolution: 28 + +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 + +MaskAssigner: + resolution: 28 + +BBoxHead: + head: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [240000, 320000] + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/mask_rcnn_se154_vd_fpn_s1x.yml b/configs/mask_rcnn_se154_vd_fpn_s1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..4430055712504bfe79252c1614792d05fb46b89b --- /dev/null +++ b/configs/mask_rcnn_se154_vd_fpn_s1x.yml @@ -0,0 +1,147 @@ +architecture: MaskRCNN +train_feed: MaskRCNNTrainFeed +eval_feed: MaskRCNNEvalFeed +test_feed: MaskRCNNTestFeed +max_iters: 260000 +snapshot_iter: 10000 +use_gpu: true +log_smooth_window: 20 +save_dir: output +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/SENet154_vd_pretrained.tar +weights: output/mask_rcnn_se154_vd_fpn_s1x/model_final/ +metric: COCO +num_classes: 81 + +MaskRCNN: + backbone: SENet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +SENet: + depth: 152 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + group_width: 4 + groups: 64 + norm_type: affine_channel + variant: d + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + anchor_generator: + aspect_ratios: [0.5, 1.0, 2.0] + variance: [1.0, 1.0, 1.0, 1.0] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + box_resolution: 7 + sampling_ratio: 2 + mask_resolution: 14 + +MaskHead: + dilation: 1 + conv_dim: 256 + num_convs: 4 + resolution: 28 + +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 + +MaskAssigner: + resolution: 28 + +BBoxHead: + head: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [200000, 240000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/mask_rcnn_x101_vd_64x4d_fpn_1x.yml b/configs/mask_rcnn_x101_vd_64x4d_fpn_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..75653ce2462e91b3be3290c5e6a5a3c12d816d71 --- /dev/null +++ b/configs/mask_rcnn_x101_vd_64x4d_fpn_1x.yml @@ -0,0 +1,146 @@ +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 +weights: output/mask_rcnn_x101_vd_64x4d_fpn_1x/model_final +metric: COCO +num_classes: 81 + +MaskRCNN: + backbone: ResNeXt + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNeXt: + depth: 101 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + group_width: 4 + groups: 64 + norm_type: affine_channel + variant: d + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + anchor_generator: + aspect_ratios: [0.5, 1.0, 2.0] + variance: [1.0, 1.0, 1.0, 1.0] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + sampling_ratio: 2 + box_resolution: 7 + mask_resolution: 14 + +MaskHead: + dilation: 1 + conv_dim: 256 + num_convs: 4 + resolution: 28 + +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 + +MaskAssigner: + resolution: 28 + +BBoxHead: + head: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [120000, 160000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/mask_rcnn_x101_vd_64x4d_fpn_2x.yml b/configs/mask_rcnn_x101_vd_64x4d_fpn_2x.yml new file mode 100644 index 0000000000000000000000000000000000000000..c5a711363330092aee30c2b4d9b08dd899f97beb --- /dev/null +++ b/configs/mask_rcnn_x101_vd_64x4d_fpn_2x.yml @@ -0,0 +1,146 @@ +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 +weights: output/mask_rcnn_x101_vd_64x4d_fpn_2x/model_final +metric: COCO +num_classes: 81 + +MaskRCNN: + backbone: ResNeXt + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: BBoxHead + bbox_assigner: BBoxAssigner + +ResNeXt: + depth: 101 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + group_width: 4 + groups: 64 + norm_type: affine_channel + variant: d + +FPN: + max_level: 6 + min_level: 2 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + +FPNRPNHead: + anchor_generator: + aspect_ratios: [0.5, 1.0, 2.0] + variance: [1.0, 1.0, 1.0, 1.0] + anchor_start_size: 32 + max_level: 6 + min_level: 2 + num_chan: 256 + 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 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + max_level: 5 + min_level: 2 + sampling_ratio: 2 + box_resolution: 7 + mask_resolution: 14 + +MaskHead: + dilation: 1 + conv_dim: 256 + num_convs: 4 + resolution: 28 + +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 + +MaskAssigner: + resolution: 28 + +BBoxHead: + head: TwoFCHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +TwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [240000, 320000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 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 new file mode 100644 index 0000000000000000000000000000000000000000..be80042520367c1bb2d63ac2998651d789c9e298 --- /dev/null +++ b/configs/obj365/cascade_rcnn_dcnv2_se154_vd_fpn_gn_cas.yml @@ -0,0 +1,247 @@ +architecture: CascadeRCNN +train_feed: FasterRCNNTrainFeed +eval_feed: FasterRCNNEvalFeed +test_feed: FasterRCNNTestFeed +max_iters: 500000 +snapshot_iter: 10000 +use_gpu: true +log_iter: 20 +log_smooth_window: 20 +save_dir: output +pretrain_weights: https://paddlemodels.bj.bcebos.com/object_detection/cascade_mask_rcnn_dcnv2_se154_vd_fpn_gn_coco_pretrained.tar +weights: output/cascade_rcnn_dcnv2_se154_vd_fpn_gn_cas/model_final +metric: COCO +num_classes: 81 + +CascadeRCNN: + backbone: ResNet + fpn: FPN + rpn_head: FPNRPNHead + roi_extractor: FPNRoIAlign + bbox_head: CascadeBBoxHead + bbox_assigner: CascadeBBoxAssigner + +SENet: + depth: 152 + feature_maps: [2, 3, 4, 5] + freeze_at: 2 + group_width: 4 + groups: 64 + norm_type: bn + freeze_norm: True + variant: d + dcn_v2_stages: [3, 4, 5] + std_senet: True + +FPN: + min_level: 2 + max_level: 6 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125, 0.25] + freeze_norm: False + norm_type: gn + +FPNRPNHead: + 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] + anchor_start_size: 32 + min_level: 2 + max_level: 6 + num_chan: 256 + rpn_target_assign: + rpn_batch_size_per_im: 256 + rpn_fg_fraction: 0.5 + rpn_positive_overlap: 0.7 + rpn_negative_overlap: 0.3 + rpn_straddle_thresh: 0.0 + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 2000 + post_nms_top_n: 2000 + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + pre_nms_top_n: 1000 + post_nms_top_n: 1000 + +FPNRoIAlign: + canconical_level: 4 + canonical_size: 224 + min_level: 2 + max_level: 5 + box_resolution: 7 + sampling_ratio: 2 + +CascadeBBoxAssigner: + batch_size_per_im: 1024 + bbox_reg_weights: [10, 20, 30] + bg_thresh_lo: [0.0, 0.0, 0.0] + bg_thresh_hi: [0.5, 0.6, 0.7] + fg_thresh: [0.5, 0.6, 0.7] + fg_fraction: 0.25 + +CascadeBBoxHead: + head: CascadeXConvNormHead + nms: + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + +CascadeXConvNormHead: + norm_type: gn + +CascadeTwoFCHead: + mlp_dim: 1024 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [400000, 460000] + - !LinearWarmup + start_factor: 0.01 + steps: 2000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + factor: 0.0001 + type: L2 + +FasterRCNNTrainFeed: + batch_size: 1 + dataset: + dataset_dir: dataset/objects365 + annotation: annotations/train.json + image_dir: train + sample_transforms: + - !DecodeImage + to_rgb: False + with_mixup: False + - !RandomFlipImage + is_mask_flip: true + is_normalized: false + prob: 0.5 + - !NormalizeImage + is_channel_first: false + is_scale: False + mean: + - 102.9801 + - 115.9465 + - 122.7717 + std: + - 1.0 + - 1.0 + - 1.0 + - !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 + max_size: 1600 + use_cv2: true + - !Permute + channel_first: true + to_bgr: false + batch_transforms: + - !PadBatch + pad_to_stride: 32 + num_workers: 4 + class_aware_sampling: true + +FasterRCNNEvalFeed: + batch_size: 1 + dataset: + dataset_dir: dataset/objects365 + annotation: annotations/val.json + image_dir: val + sample_transforms: + - !DecodeImage + to_rgb: False + with_mixup: False + - !NormalizeImage + is_channel_first: false + is_scale: False + mean: + - 102.9801 + - 115.9465 + - 122.7717 + std: + - 1.0 + - 1.0 + - 1.0 + - !ResizeImage + target_size: 800 + max_size: 1333 + interp: 1 + - !Permute + channel_first: true + to_bgr: false + batch_transforms: + - !PadBatch + pad_to_stride: 32 + +FasterRCNNTestFeed: + batch_size: 1 + dataset: + annotation: 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 + - 115.9465 + - 122.7717 + std: + - 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 diff --git a/configs/retinanet_r101_fpn_1x.yml b/configs/retinanet_r101_fpn_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..1864f379830292f1cc6a659995a71783688f61c3 --- /dev/null +++ b/configs/retinanet_r101_fpn_1x.yml @@ -0,0 +1,105 @@ +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 +weights: output/retinanet_r101_fpn_1x/model_final +log_smooth_window: 20 +snapshot_iter: 10000 +metric: COCO +save_dir: output +num_classes: 81 + +RetinaNet: + backbone: ResNet + fpn: FPN + retina_head: RetinaHead + +ResNet: + norm_type: affine_channel + norm_decay: 0. + depth: 101 + feature_maps: [3, 4, 5] + freeze_at: 2 + +FPN: + max_level: 7 + min_level: 3 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125] + has_extra_convs: true + +RetinaHead: + num_convs_per_octave: 4 + num_chan: 256 + max_level: 7 + min_level: 3 + prior_prob: 0.01 + base_scale: 4 + num_scales_per_octave: 3 + anchor_generator: + aspect_ratios: [1.0, 2.0, 0.5] + variance: [1.0, 1.0, 1.0, 1.0] + target_assign: + positive_overlap: 0.5 + negative_overlap: 0.4 + gamma: 2.0 + alpha: 0.25 + sigma: 3.0151134457776365 + output_decoder: + score_thresh: 0.05 + nms_thresh: 0.5 + pre_nms_top_n: 1000 + detections_per_im: 100 + nms_eta: 1.0 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [60000, 80000] + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + factor: 0.0001 + type: L2 + +FasterRCNNTrainFeed: + 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: + 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: + 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 new file mode 100644 index 0000000000000000000000000000000000000000..8c24ef4defd9e947406591a17ae2feb4833c61a1 --- /dev/null +++ b/configs/retinanet_r50_fpn_1x.yml @@ -0,0 +1,105 @@ +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 +weights: output/retinanet_r50_fpn_1x/model_final +log_smooth_window: 20 +snapshot_iter: 10000 +metric: COCO +save_dir: output +num_classes: 81 + +RetinaNet: + backbone: ResNet + fpn: FPN + retina_head: RetinaHead + +ResNet: + norm_type: affine_channel + norm_decay: 0. + depth: 50 + feature_maps: [3, 4, 5] + freeze_at: 2 + +FPN: + max_level: 7 + min_level: 3 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125] + has_extra_convs: true + +RetinaHead: + num_convs_per_octave: 4 + num_chan: 256 + max_level: 7 + min_level: 3 + prior_prob: 0.01 + base_scale: 4 + num_scales_per_octave: 3 + anchor_generator: + aspect_ratios: [1.0, 2.0, 0.5] + variance: [1.0, 1.0, 1.0, 1.0] + target_assign: + positive_overlap: 0.5 + negative_overlap: 0.4 + gamma: 2.0 + alpha: 0.25 + sigma: 3.0151134457776365 + output_decoder: + score_thresh: 0.05 + nms_thresh: 0.5 + pre_nms_top_n: 1000 + detections_per_im: 100 + nms_eta: 1.0 + +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [60000, 80000] + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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: 128 + num_workers: 2 + +FasterRCNNEvalFeed: + 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: + 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 new file mode 100644 index 0000000000000000000000000000000000000000..6cc33fafa485c4a64cefef67ec0d6ab0b237db84 --- /dev/null +++ b/configs/retinanet_x101_vd_64x4d_fpn_1x.yml @@ -0,0 +1,108 @@ +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 +weights: output/retinanet_x101_vd_64x4d_fpn_1x/model_final +log_smooth_window: 20 +log_iter: 20 +snapshot_iter: 30000 +metric: COCO +save_dir: output +num_classes: 81 + +RetinaNet: + backbone: ResNeXt + fpn: FPN + retina_head: RetinaHead + +ResNeXt: + depth: 101 + feature_maps: [3, 4, 5] + freeze_at: 2 + group_width: 4 + groups: 64 + norm_type: bn + variant: d + +FPN: + max_level: 7 + min_level: 3 + num_chan: 256 + spatial_scale: [0.03125, 0.0625, 0.125] + has_extra_convs: true + +RetinaHead: + num_convs_per_octave: 4 + num_chan: 256 + max_level: 7 + min_level: 3 + prior_prob: 0.01 + base_scale: 4 + num_scales_per_octave: 3 + anchor_generator: + aspect_ratios: [1.0, 2.0, 0.5] + variance: [1.0, 1.0, 1.0, 1.0] + target_assign: + positive_overlap: 0.5 + negative_overlap: 0.4 + gamma: 2.0 + alpha: 0.25 + sigma: 3.0151134457776365 + output_decoder: + score_thresh: 0.05 + nms_thresh: 0.5 + pre_nms_top_n: 1000 + detections_per_im: 100 + nms_eta: 1.0 + +LearningRate: + base_lr: 0.005 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [120000, 160000] + - !LinearWarmup + start_factor: 0.1 + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + factor: 0.0001 + type: L2 + +FasterRCNNTrainFeed: + batch_size: 1 + dataset: + dataset_dir: dataset/coco + annotation: annotations/instances_train2017.json + image_dir: train2017 + 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 + batch_transforms: + - !PadBatch + pad_to_stride: 128 + num_workers: 2 + +FasterRCNNTestFeed: + 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/ssd/ssd_mobilenet_v1_voc.yml b/configs/ssd/ssd_mobilenet_v1_voc.yml new file mode 100644 index 0000000000000000000000000000000000000000..a360830f05f4a1d56bdb915fd95e9a905460c8e5 --- /dev/null +++ b/configs/ssd/ssd_mobilenet_v1_voc.yml @@ -0,0 +1,80 @@ +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 +snapshot_iter: 2000 +log_smooth_window: 1 +metric: VOC +map_type: 11point +save_dir: output +weights: output/ssd_mobilenet_v1_voc/model_final/ +# 20(label_class) + 1(background) +num_classes: 21 + +SSD: + backbone: MobileNet + multi_box_head: MultiBoxHead + output_decoder: + background_label: 0 + keep_top_k: 200 + nms_eta: 1.0 + nms_threshold: 0.45 + nms_top_k: 400 + score_threshold: 0.01 + +MobileNet: + norm_decay: 0. + conv_group_scale: 1 + conv_learning_rate: 0.1 + extra_block_filters: [[256, 512], [128, 256], [128, 256], [64, 128]] + with_extra_blocks: true + +MultiBoxHead: + aspect_ratios: [[2.], [2., 3.], [2., 3.], [2., 3.], [2., 3.], [2., 3.]] + base_size: 300 + flip: true + max_ratio: 90 + max_sizes: [[], 150.0, 195.0, 240.0, 285.0, 300.0] + min_ratio: 20 + min_sizes: [60.0, 105.0, 150.0, 195.0, 240.0, 285.0] + offset: 0.5 + +LearningRate: + schedulers: + - !PiecewiseDecay + milestones: [10000, 15000, 20000, 25000] + values: [0.001, 0.0005, 0.00025, 0.0001, 0.00001] + +OptimizerBuilder: + optimizer: + momentum: 0.0 + type: RMSPropOptimizer + regularizer: + factor: 0.00005 + type: L2 + +SSDTrainFeed: + batch_size: 32 + use_process: true + dataset: + dataset_dir: dataset/voc + annotation: trainval.txt + use_default_label: true + +SSDEvalFeed: + batch_size: 64 + use_process: true + dataset: + dataset_dir: dataset/voc + annotation: test.txt + use_default_label: true + drop_last: false + +SSDTestFeed: + batch_size: 1 + dataset: + use_default_label: true + drop_last: false diff --git a/configs/ssd/ssd_vgg16_300.yml b/configs/ssd/ssd_vgg16_300.yml new file mode 100644 index 0000000000000000000000000000000000000000..f5e987bcb156a3a4973f9ae9d376d38597471ce2 --- /dev/null +++ b/configs/ssd/ssd_vgg16_300.yml @@ -0,0 +1,149 @@ +architecture: SSD +train_feed: SSDTrainFeed +eval_feed: SSDEvalFeed +test_feed: SSDTestFeed +use_gpu: true +max_iters: 400000 +snapshot_iter: 10000 +log_smooth_window: 20 +log_iter: 20 +metric: COCO +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/VGG16_caffe_pretrained.tar +save_dir: output +weights: output/ssd_vgg16_300/model_final +num_classes: 81 + +SSD: + backbone: VGG + multi_box_head: MultiBoxHead + output_decoder: + background_label: 0 + keep_top_k: 200 + nms_eta: 1.0 + nms_threshold: 0.45 + nms_top_k: 400 + score_threshold: 0.01 + +VGG: + depth: 16 + with_extra_blocks: true + normalizations: [20., -1, -1, -1, -1, -1] + +MultiBoxHead: + base_size: 300 + aspect_ratios: [[2.], [2., 3.], [2., 3.], [2., 3.], [2.], [2.]] + min_ratio: 15 + max_ratio: 90 + min_sizes: [30.0, 60.0, 111.0, 162.0, 213.0, 264.0] + max_sizes: [60.0, 111.0, 162.0, 213.0, 264.0, 315.0] + steps: [8, 16, 32, 64, 100, 300] + offset: 0.5 + flip: true + kernel_size: 3 + pad: 1 + +LearningRate: + base_lr: 0.001 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [280000, 360000] + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + factor: 0.0005 + type: L2 + +SSDTrainFeed: + batch_size: 8 + dataset: + 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 + - !ResizeImage + interp: 1 + target_size: 300 + use_cv2: false + - !RandomFlipImage + is_normalized: true + - !Permute + to_bgr: false + - !NormalizeImage + is_scale: false + mean: [104, 117, 123] + std: [1, 1, 1] + +SSDEvalFeed: + batch_size: 16 + dataset: + dataset_dir: dataset/coco + annotation: annotations/instances_val2017.json + image_dir: val2017 + drop_last: false + image_shape: [3, 300, 300] + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !ResizeImage + interp: 1 + target_size: 300 + use_cv2: false + - !Permute + to_bgr: false + - !NormalizeImage + is_scale: false + mean: [104, 117, 123] + std: [1, 1, 1] + +SSDTestFeed: + batch_size: 1 + dataset: + annotation: dataset/coco/annotations/instances_val2017.json + image_shape: [3, 300, 300] + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !ResizeImage + interp: 1 + max_size: 0 + target_size: 300 + use_cv2: false + - !Permute + to_bgr: false + - !NormalizeImage + is_scale: false + mean: [104, 117, 123] + std: [1, 1, 1] diff --git a/configs/ssd/ssd_vgg16_300_voc.yml b/configs/ssd/ssd_vgg16_300_voc.yml new file mode 100644 index 0000000000000000000000000000000000000000..36c0537591f7771eb9c4814cdd8e4c77338e6a9f --- /dev/null +++ b/configs/ssd/ssd_vgg16_300_voc.yml @@ -0,0 +1,151 @@ +architecture: SSD +train_feed: SSDTrainFeed +eval_feed: SSDEvalFeed +test_feed: SSDTestFeed +use_gpu: true +max_iters: 120001 +snapshot_iter: 10000 +log_smooth_window: 20 +log_iter: 20 +metric: VOC +map_type: 11point +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/VGG16_caffe_pretrained.tar +save_dir: output +weights: output/ssd_vgg16_300_voc/model_final/ +# 20(label_class) + 1(background) +num_classes: 21 + +SSD: + backbone: VGG + multi_box_head: MultiBoxHead + output_decoder: + background_label: 0 + keep_top_k: 200 + nms_eta: 1.0 + nms_threshold: 0.45 + nms_top_k: 400 + score_threshold: 0.01 + +VGG: + depth: 16 + with_extra_blocks: true + normalizations: [20., -1, -1, -1, -1, -1] + +MultiBoxHead: + base_size: 300 + aspect_ratios: [[2.], [2., 3.], [2., 3.], [2., 3.], [2.], [2.]] + min_ratio: 20 + max_ratio: 90 + min_sizes: [30.0, 60.0, 111.0, 162.0, 213.0, 264.0] + max_sizes: [60.0, 111.0, 162.0, 213.0, 264.0, 315.0] + steps: [8, 16, 32, 64, 100, 300] + offset: 0.5 + flip: true + min_max_aspect_ratios_order: true + kernel_size: 3 + pad: 1 + +LearningRate: + base_lr: 0.001 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [80000, 100000] + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + factor: 0.0005 + type: L2 + +SSDTrainFeed: + batch_size: 8 + dataset: + dataset_dir: dataset/voc + annotation: 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 + - !ResizeImage + interp: 1 + target_size: 300 + use_cv2: False + - !RandomFlipImage + is_normalized: true + - !Permute + to_bgr: false + - !NormalizeImage + is_scale: false + mean: [104, 117, 123] + std: [1, 1, 1] + +SSDEvalFeed: + batch_size: 32 + dataset: + 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 + with_mixup: false + - !NormalizeBox {} + - !ResizeImage + interp: 1 + target_size: 300 + use_cv2: false + - !Permute + to_bgr: false + - !NormalizeImage + is_scale: false + mean: [104, 117, 123] + std: [1, 1, 1] + +SSDTestFeed: + batch_size: 1 + dataset: + use_default_label: true + drop_last: false + image_shape: [3, 300, 300] + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !ResizeImage + interp: 1 + max_size: 0 + target_size: 300 + use_cv2: false + - !Permute + to_bgr: false + - !NormalizeImage + is_scale: false + mean: [104, 117, 123] + std: [1, 1, 1] diff --git a/configs/ssd/ssd_vgg16_512.yml b/configs/ssd/ssd_vgg16_512.yml new file mode 100644 index 0000000000000000000000000000000000000000..6214327642bfe3beb09e28cd7dbe891e8d49b848 --- /dev/null +++ b/configs/ssd/ssd_vgg16_512.yml @@ -0,0 +1,151 @@ +architecture: SSD +train_feed: SSDTrainFeed +eval_feed: SSDEvalFeed +test_feed: SSDTestFeed +use_gpu: true +max_iters: 400000 +snapshot_iter: 10000 +log_smooth_window: 20 +log_iter: 20 +metric: COCO +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/VGG16_caffe_pretrained.tar +save_dir: output +weights: output/ssd_vgg16_512/model_final +num_classes: 81 + +SSD: + backbone: VGG + multi_box_head: MultiBoxHead + output_decoder: + background_label: 0 + keep_top_k: 200 + nms_eta: 1.0 + nms_threshold: 0.45 + nms_top_k: 400 + score_threshold: 0.01 + +VGG: + depth: 16 + with_extra_blocks: true + normalizations: [20., -1, -1, -1, -1, -1, -1] + extra_block_filters: [[256, 512, 1, 2, 3], [128, 256, 1, 2, 3], [128, 256, 1, 2, 3], [128, 256, 1, 2, 3], [128, 256, 1, 1, 4]] + + +MultiBoxHead: + base_size: 512 + aspect_ratios: [[2.], [2., 3.], [2., 3.], [2., 3.], [2., 3.], [2.], [2.]] + min_ratio: 15 + max_ratio: 90 + min_sizes: [20.0, 51.0, 133.0, 215.0, 296.0, 378.0, 460.0] + max_sizes: [51.0, 133.0, 215.0, 296.0, 378.0, 460.0, 542.0] + steps: [8, 16, 32, 64, 128, 256, 512] + offset: 0.5 + flip: true + kernel_size: 3 + pad: 1 + +LearningRate: + base_lr: 0.001 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [280000, 360000] + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + factor: 0.0005 + type: L2 + +SSDTrainFeed: + batch_size: 8 + dataset: + 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 + - !ResizeImage + interp: 1 + target_size: 512 + use_cv2: false + - !RandomFlipImage + is_normalized: true + - !Permute + to_bgr: false + - !NormalizeImage + is_scale: false + mean: [104, 117, 123] + std: [1, 1, 1] + +SSDEvalFeed: + batch_size: 8 + dataset: + dataset_dir: dataset/coco + annotation: annotations/instances_val2017.json + image_dir: val2017 + drop_last: false + image_shape: [3, 512, 512] + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !ResizeImage + interp: 1 + target_size: 512 + use_cv2: false + - !Permute + to_bgr: false + - !NormalizeImage + is_scale: false + mean: [104, 117, 123] + std: [1, 1, 1] + +SSDTestFeed: + batch_size: 1 + dataset: + annotation: dataset/coco/annotations/instances_val2017.json + image_shape: [3, 512, 512] + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !ResizeImage + interp: 1 + max_size: 0 + target_size: 512 + use_cv2: false + - !Permute + to_bgr: false + - !NormalizeImage + is_scale: false + mean: [104, 117, 123] + std: [1, 1, 1] diff --git a/configs/ssd/ssd_vgg16_512_voc.yml b/configs/ssd/ssd_vgg16_512_voc.yml new file mode 100644 index 0000000000000000000000000000000000000000..b2028e0b11e6d05d061788315de2630ac0ede52e --- /dev/null +++ b/configs/ssd/ssd_vgg16_512_voc.yml @@ -0,0 +1,155 @@ +architecture: SSD +train_feed: SSDTrainFeed +eval_feed: SSDEvalFeed +test_feed: SSDTestFeed +use_gpu: true +max_iters: 120000 +snapshot_iter: 10000 +log_smooth_window: 20 +log_iter: 20 +metric: VOC +map_type: 11point +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/VGG16_caffe_pretrained.tar +save_dir: output +weights: output/ssd_vgg16_512_voc/model_final/ +# 20(label_class) + 1(background) +num_classes: 21 + +SSD: + backbone: VGG + multi_box_head: MultiBoxHead + output_decoder: + background_label: 0 + keep_top_k: 200 + nms_eta: 1.0 + nms_threshold: 0.45 + nms_top_k: 400 + score_threshold: 0.01 + +VGG: + depth: 16 + with_extra_blocks: true + normalizations: [20., -1, -1, -1, -1, -1, -1] + extra_block_filters: [[256, 512, 1, 2, 3], [128, 256, 1, 2, 3], [128, 256, 1, 2, 3], [128, 256, 1, 2, 3], [128, 256, 1, 1, 4]] + + +MultiBoxHead: + base_size: 512 + aspect_ratios: [[2.], [2., 3.], [2., 3.], [2., 3.], [2., 3.], [2.], [2.]] + min_ratio: 20 + max_ratio: 90 + min_sizes: [20.0, 51.0, 133.0, 215.0, 296.0, 378.0, 460.0] + max_sizes: [51.0, 133.0, 215.0, 296.0, 378.0, 460.0, 542.0] + steps: [8, 16, 32, 64, 128, 256, 512] + offset: 0.5 + flip: true + kernel_size: 3 + pad: 1 + +LearningRate: + base_lr: 0.001 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [80000, 100000] + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + factor: 0.0005 + type: L2 + +SSDTrainFeed: + batch_size: 8 + dataset: + dataset_dir: dataset/voc + annotation: 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 + - !ResizeImage + interp: 1 + target_size: 512 + use_cv2: false + - !RandomFlipImage + is_normalized: true + - !Permute + to_bgr: false + - !NormalizeImage + is_scale: false + mean: [123, 117, 104] + std: [1, 1, 1] + +SSDEvalFeed: + batch_size: 32 + dataset: + 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 + with_mixup: false + - !NormalizeBox {} + - !ResizeImage + interp: 1 + target_size: 512 + use_cv2: false + - !Permute + to_bgr: false + - !NormalizeImage + is_scale: false + mean: [123, 117, 104] + std: [1, 1, 1] + +SSDTestFeed: + batch_size: 1 + dataset: + use_default_label: true + drop_last: false + image_shape: [3, 512, 512] + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !ResizeImage + interp: 1 + max_size: 0 + target_size: 512 + use_cv2: false + - !Permute + to_bgr: false + - !NormalizeImage + is_scale: false + mean: [123, 117, 104] + std: [1, 1, 1] diff --git a/configs/yolov3_darknet.yml b/configs/yolov3_darknet.yml new file mode 100644 index 0000000000000000000000000000000000000000..9a1c243b8b245c4d0cb60e6ea9f57778e896d263 --- /dev/null +++ b/configs/yolov3_darknet.yml @@ -0,0 +1,82 @@ +architecture: YOLOv3 +train_feed: YoloTrainFeed +eval_feed: YoloEvalFeed +test_feed: YoloTestFeed +use_gpu: true +max_iters: 500200 +log_smooth_window: 20 +save_dir: output +snapshot_iter: 2000 +metric: COCO +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/DarkNet53_pretrained.tar +weights: output/yolov3_darknet/model_final +num_classes: 80 + +YOLOv3: + backbone: DarkNet + yolo_head: YOLOv3Head + +DarkNet: + norm_type: sync_bn + norm_decay: 0. + depth: 53 + +YOLOv3Head: + anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]] + anchors: [[10, 13], [16, 30], [33, 23], + [30, 61], [62, 45], [59, 119], + [116, 90], [156, 198], [373, 326]] + norm_decay: 0. + ignore_thresh: 0.7 + label_smooth: true + nms: + background_label: -1 + keep_top_k: 100 + nms_threshold: 0.45 + nms_top_k: 1000 + normalized: false + score_threshold: 0.01 + +LearningRate: + base_lr: 0.001 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: + - 400000 + - 450000 + - !LinearWarmup + start_factor: 0. + steps: 4000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/yolov3_darknet_voc.yml b/configs/yolov3_darknet_voc.yml new file mode 100644 index 0000000000000000000000000000000000000000..876d380ffcd4f59bcdeebd8fa0e51e98a1e58f93 --- /dev/null +++ b/configs/yolov3_darknet_voc.yml @@ -0,0 +1,84 @@ +architecture: YOLOv3 +train_feed: YoloTrainFeed +eval_feed: YoloEvalFeed +test_feed: YoloTestFeed +use_gpu: true +max_iters: 70000 +log_smooth_window: 20 +save_dir: output +snapshot_iter: 2000 +metric: VOC +map_type: 11point +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/DarkNet53_pretrained.tar +weights: output/yolov3_darknet_voc/model_final +num_classes: 20 + +YOLOv3: + backbone: DarkNet + yolo_head: YOLOv3Head + +DarkNet: + norm_type: sync_bn + norm_decay: 0. + depth: 53 + +YOLOv3Head: + anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]] + anchors: [[10, 13], [16, 30], [33, 23], + [30, 61], [62, 45], [59, 119], + [116, 90], [156, 198], [373, 326]] + norm_decay: 0. + ignore_thresh: 0.7 + label_smooth: false + nms: + background_label: -1 + keep_top_k: 100 + nms_threshold: 0.45 + nms_top_k: 1000 + normalized: false + score_threshold: 0.01 + +LearningRate: + base_lr: 0.001 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: + - 55000 + - 62000 + - !LinearWarmup + start_factor: 0. + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + factor: 0.0005 + type: L2 + +YoloTrainFeed: + batch_size: 8 + dataset: + dataset_dir: dataset/voc + annotation: trainval.txt + use_default_label: true + num_workers: 8 + bufsize: 128 + use_process: true + mixup_epoch: 250 + +YoloEvalFeed: + batch_size: 8 + image_shape: [3, 608, 608] + dataset: + dataset_dir: dataset/voc + annotation: test.txt + use_default_label: true + +YoloTestFeed: + batch_size: 1 + image_shape: [3, 608, 608] + dataset: + use_default_label: true diff --git a/configs/yolov3_mobilenet_v1.yml b/configs/yolov3_mobilenet_v1.yml new file mode 100644 index 0000000000000000000000000000000000000000..3e622025b587b56c6e79dd3a1cf1cbba00901406 --- /dev/null +++ b/configs/yolov3_mobilenet_v1.yml @@ -0,0 +1,83 @@ +architecture: YOLOv3 +train_feed: YoloTrainFeed +eval_feed: YoloEvalFeed +test_feed: YoloTestFeed +use_gpu: true +max_iters: 500200 +log_smooth_window: 20 +save_dir: output +snapshot_iter: 2000 +metric: COCO +pretrain_weights: http://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV1_pretrained.tar +weights: output/yolov3_mobilenet_v1/model_final +num_classes: 80 + +YOLOv3: + backbone: MobileNet + yolo_head: YOLOv3Head + +MobileNet: + norm_type: sync_bn + norm_decay: 0. + conv_group_scale: 1 + with_extra_blocks: false + +YOLOv3Head: + anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]] + anchors: [[10, 13], [16, 30], [33, 23], + [30, 61], [62, 45], [59, 119], + [116, 90], [156, 198], [373, 326]] + norm_decay: 0. + ignore_thresh: 0.7 + label_smooth: true + nms: + background_label: -1 + keep_top_k: 100 + nms_threshold: 0.45 + nms_top_k: 1000 + normalized: false + score_threshold: 0.01 + +LearningRate: + base_lr: 0.001 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: + - 400000 + - 450000 + - !LinearWarmup + start_factor: 0. + steps: 4000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/yolov3_mobilenet_v1_fruit.yml b/configs/yolov3_mobilenet_v1_fruit.yml new file mode 100644 index 0000000000000000000000000000000000000000..bbbcd7fba36b6eab5789ef77466286713e49ca94 --- /dev/null +++ b/configs/yolov3_mobilenet_v1_fruit.yml @@ -0,0 +1,122 @@ +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: 200 +metric: VOC +map_type: 11point +pretrain_weights: https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1.tar +weights: output/yolov3_mobilenet_v1_fruit/best_model +num_classes: 3 +finetune_exclude_pretrained_params: ['yolo_output'] + +YOLOv3: + backbone: MobileNet + yolo_head: YOLOv3Head + +MobileNet: + norm_type: sync_bn + norm_decay: 0. + conv_group_scale: 1 + with_extra_blocks: false + +YOLOv3Head: + anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]] + anchors: [[10, 13], [16, 30], [33, 23], + [30, 61], [62, 45], [59, 119], + [116, 90], [156, 198], [373, 326]] + norm_decay: 0. + ignore_thresh: 0.7 + label_smooth: true + nms: + background_label: -1 + keep_top_k: 100 + nms_threshold: 0.45 + nms_top_k: 1000 + normalized: false + score_threshold: 0.01 + +LearningRate: + base_lr: 0.00001 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: + - 15000 + - 18000 + - !LinearWarmup + start_factor: 0. + steps: 100 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + factor: 0.0005 + type: L2 + +YoloTrainFeed: + batch_size: 1 + dataset: + dataset_dir: dataset/fruit/fruit-detection + annotation: train.txt + use_default_label: false + num_workers: 16 + bufsize: 128 + use_process: true + mixup_epoch: -1 + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !NormalizeBox {} + - !ExpandImage + max_ratio: 4.0 + mean: [123.675, 116.28, 103.53] + prob: 0.5 + - !RandomInterpImage + max_size: 0 + target_size: 608 + - !RandomFlipImage + is_mask_flip: false + is_normalized: true + 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 + - !Permute + channel_first: true + to_bgr: false + batch_transforms: + - !RandomShape + sizes: [608] + with_background: false + +YoloEvalFeed: + batch_size: 1 + image_shape: [3, 608, 608] + dataset: + dataset_dir: dataset/fruit/fruit-detection + annotation: val.txt + use_default_label: false + + +YoloTestFeed: + batch_size: 1 + image_shape: [3, 608, 608] + dataset: + dataset_dir: dataset/fruit/fruit-detection + use_default_label: false diff --git a/configs/yolov3_mobilenet_v1_voc.yml b/configs/yolov3_mobilenet_v1_voc.yml new file mode 100644 index 0000000000000000000000000000000000000000..63e4cbea93f8a89b4f7890d4ecf66213eb578f66 --- /dev/null +++ b/configs/yolov3_mobilenet_v1_voc.yml @@ -0,0 +1,85 @@ +architecture: YOLOv3 +train_feed: YoloTrainFeed +eval_feed: YoloEvalFeed +test_feed: YoloTestFeed +use_gpu: true +max_iters: 70000 +log_smooth_window: 20 +save_dir: output +snapshot_iter: 2000 +metric: VOC +map_type: 11point +pretrain_weights: http://paddle-imagenet-models-name.bj.bcebos.com/MobileNetV1_pretrained.tar +weights: output/yolov3_mobilenet_v1_voc/model_final +num_classes: 20 + +YOLOv3: + backbone: MobileNet + yolo_head: YOLOv3Head + +MobileNet: + norm_type: sync_bn + norm_decay: 0. + conv_group_scale: 1 + with_extra_blocks: false + +YOLOv3Head: + anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]] + anchors: [[10, 13], [16, 30], [33, 23], + [30, 61], [62, 45], [59, 119], + [116, 90], [156, 198], [373, 326]] + norm_decay: 0. + ignore_thresh: 0.7 + label_smooth: false + nms: + background_label: -1 + keep_top_k: 100 + nms_threshold: 0.45 + nms_top_k: 1000 + normalized: false + score_threshold: 0.01 + +LearningRate: + base_lr: 0.001 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: + - 55000 + - 62000 + - !LinearWarmup + start_factor: 0. + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + factor: 0.0005 + type: L2 + +YoloTrainFeed: + batch_size: 8 + dataset: + dataset_dir: dataset/voc + annotation: trainval.txt + use_default_label: true + num_workers: 8 + bufsize: 128 + use_process: true + mixup_epoch: 250 + +YoloEvalFeed: + batch_size: 8 + image_shape: [3, 608, 608] + dataset: + dataset_dir: dataset/voc + annotation: test.txt + use_default_label: true + +YoloTestFeed: + batch_size: 1 + image_shape: [3, 608, 608] + dataset: + use_default_label: true diff --git a/configs/yolov3_r34.yml b/configs/yolov3_r34.yml new file mode 100644 index 0000000000000000000000000000000000000000..e864f8fd92d2f671c442f3beecd8344171952e48 --- /dev/null +++ b/configs/yolov3_r34.yml @@ -0,0 +1,85 @@ +architecture: YOLOv3 +train_feed: YoloTrainFeed +eval_feed: YoloEvalFeed +test_feed: YoloTestFeed +use_gpu: true +max_iters: 500200 +log_smooth_window: 20 +save_dir: output +snapshot_iter: 2000 +metric: COCO +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet34_pretrained.tar +weights: output/yolov3_r34/model_final +num_classes: 80 + +YOLOv3: + backbone: ResNet + yolo_head: YOLOv3Head + +ResNet: + norm_type: sync_bn + freeze_at: 0 + freeze_norm: false + norm_decay: 0. + depth: 34 + feature_maps: [3, 4, 5] + +YOLOv3Head: + anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]] + anchors: [[10, 13], [16, 30], [33, 23], + [30, 61], [62, 45], [59, 119], + [116, 90], [156, 198], [373, 326]] + norm_decay: 0. + ignore_thresh: 0.7 + label_smooth: true + nms: + background_label: -1 + keep_top_k: 100 + nms_threshold: 0.45 + nms_top_k: 1000 + normalized: false + score_threshold: 0.01 + +LearningRate: + base_lr: 0.001 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: + - 400000 + - 450000 + - !LinearWarmup + start_factor: 0. + steps: 4000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + 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 diff --git a/configs/yolov3_r34_voc.yml b/configs/yolov3_r34_voc.yml new file mode 100644 index 0000000000000000000000000000000000000000..aa152e8fe88fb5eb3a86ac5b1623ce940ad81b1e --- /dev/null +++ b/configs/yolov3_r34_voc.yml @@ -0,0 +1,87 @@ +architecture: YOLOv3 +train_feed: YoloTrainFeed +eval_feed: YoloEvalFeed +test_feed: YoloTestFeed +use_gpu: true +max_iters: 70000 +log_smooth_window: 20 +save_dir: output +snapshot_iter: 2000 +metric: VOC +map_type: 11point +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet34_pretrained.tar +weights: output/yolov3_r34_voc/model_final +num_classes: 20 + +YOLOv3: + backbone: ResNet + yolo_head: YOLOv3Head + +ResNet: + norm_type: sync_bn + freeze_at: 0 + freeze_norm: false + norm_decay: 0. + depth: 34 + feature_maps: [3, 4, 5] + +YOLOv3Head: + anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]] + anchors: [[10, 13], [16, 30], [33, 23], + [30, 61], [62, 45], [59, 119], + [116, 90], [156, 198], [373, 326]] + norm_decay: 0. + ignore_thresh: 0.7 + label_smooth: false + nms: + background_label: -1 + keep_top_k: 100 + nms_threshold: 0.45 + nms_top_k: 1000 + normalized: false + score_threshold: 0.01 + +LearningRate: + base_lr: 0.001 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: + - 55000 + - 62000 + - !LinearWarmup + start_factor: 0. + steps: 1000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + factor: 0.0005 + type: L2 + +YoloTrainFeed: + batch_size: 8 + dataset: + dataset_dir: dataset/voc + annotation: trainval.txt + use_default_label: true + num_workers: 8 + bufsize: 128 + use_process: true + mixup_epoch: 250 + +YoloEvalFeed: + batch_size: 8 + image_shape: [3, 608, 608] + dataset: + dataset_dir: dataset/voc + annotation: test.txt + use_default_label: true + +YoloTestFeed: + batch_size: 1 + image_shape: [3, 608, 608] + dataset: + use_default_label: true diff --git a/contrib/PedestrianDetection/demo/001.png b/contrib/PedestrianDetection/demo/001.png new file mode 100644 index 0000000000000000000000000000000000000000..63ae9167fd03e8a95756fe5f6195fc8d741b9cfa Binary files /dev/null and b/contrib/PedestrianDetection/demo/001.png differ diff --git a/contrib/PedestrianDetection/demo/002.png b/contrib/PedestrianDetection/demo/002.png new file mode 100644 index 0000000000000000000000000000000000000000..0de905cf55e6b02487ee1b8220810df8eaa24c2c Binary files /dev/null and b/contrib/PedestrianDetection/demo/002.png differ diff --git a/contrib/PedestrianDetection/demo/003.png b/contrib/PedestrianDetection/demo/003.png new file mode 100644 index 0000000000000000000000000000000000000000..e9026e099df42d4267be07a71401eb5426b47745 Binary files /dev/null and b/contrib/PedestrianDetection/demo/003.png differ diff --git a/contrib/PedestrianDetection/demo/004.png b/contrib/PedestrianDetection/demo/004.png new file mode 100644 index 0000000000000000000000000000000000000000..d8118ec3e0ef63bc74e825b5e7638a1886580604 Binary files /dev/null and b/contrib/PedestrianDetection/demo/004.png differ diff --git a/contrib/PedestrianDetection/demo/output/001.png b/contrib/PedestrianDetection/demo/output/001.png new file mode 100644 index 0000000000000000000000000000000000000000..5194d6ff891b9507fedfc53f36de4f00219c7f30 Binary files /dev/null and b/contrib/PedestrianDetection/demo/output/001.png differ diff --git a/contrib/PedestrianDetection/demo/output/004.png b/contrib/PedestrianDetection/demo/output/004.png new file mode 100644 index 0000000000000000000000000000000000000000..7c62be5051f9a47c5f5e98ccd9f45c3fa5f30257 Binary files /dev/null and b/contrib/PedestrianDetection/demo/output/004.png differ diff --git a/contrib/PedestrianDetection/pedestrian.json b/contrib/PedestrianDetection/pedestrian.json new file mode 100644 index 0000000000000000000000000000000000000000..f72fe6dc65209ab3506d18556fb8b83b6ec832a9 --- /dev/null +++ b/contrib/PedestrianDetection/pedestrian.json @@ -0,0 +1,11 @@ +{ + "images": [], + "annotations": [], + "categories": [ + { + "supercategory": "component", + "id": 1, + "name": "pedestrian" + } + ] +} diff --git a/contrib/PedestrianDetection/pedestrian_yolov3_darknet.yml b/contrib/PedestrianDetection/pedestrian_yolov3_darknet.yml new file mode 100644 index 0000000000000000000000000000000000000000..adc9109aa356e109afc81bea13b856ce0f4be448 --- /dev/null +++ b/contrib/PedestrianDetection/pedestrian_yolov3_darknet.yml @@ -0,0 +1,82 @@ +architecture: YOLOv3 +train_feed: YoloTrainFeed +eval_feed: YoloEvalFeed +test_feed: YoloTestFeed +use_gpu: true +max_iters: 200000 +log_smooth_window: 20 +save_dir: output +snapshot_iter: 5000 +metric: COCO +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/DarkNet53_pretrained.tar +weights: https://paddlemodels.bj.bcebos.com/object_detection/pedestrian_yolov3_darknet.tar +num_classes: 1 + +YOLOv3: + backbone: DarkNet + yolo_head: YOLOv3Head + +DarkNet: + norm_type: sync_bn + norm_decay: 0. + depth: 53 + +YOLOv3Head: + anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]] + anchors: [[10, 13], [16, 30], [33, 23], + [30, 61], [62, 45], [59, 119], + [116, 90], [156, 198], [373, 326]] + norm_decay: 0. + ignore_thresh: 0.7 + label_smooth: true + nms: + background_label: -1 + keep_top_k: 100 + nms_threshold: 0.45 + nms_top_k: 1000 + normalized: false + score_threshold: 0.01 + +LearningRate: + base_lr: 0.001 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: + - 150000 + - 180000 + - !LinearWarmup + start_factor: 0. + steps: 4000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + factor: 0.0005 + type: L2 + +YoloTrainFeed: + batch_size: 8 + dataset: + dataset_dir: dataset/pedestrian + 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/pedestrian + annotation: annotations/instances_val2017.json + image_dir: val2017 + +YoloTestFeed: + batch_size: 1 + image_shape: [3, 608, 608] + dataset: + annotation: contrib/PedestrianDetection/pedestrian.json diff --git a/contrib/README.md b/contrib/README.md new file mode 100644 index 0000000000000000000000000000000000000000..fbb55512acec372fa31d2955afe65cbc8abf7b2e --- /dev/null +++ b/contrib/README.md @@ -0,0 +1,104 @@ +# PaddleDetection applied for specific scenarios + +We provide some models implemented by PaddlePaddle to detect objects in specific scenarios, users can download the models and use them in these scenarios. + +| Task | Algorithm | Box AP | Download | +|:---------------------|:---------:|:------:| :-------------------------------------------------------------------------------------: | +| Vehicle Detection | YOLOv3 | 54.5 | [model](https://paddlemodels.bj.bcebos.com/object_detection/vehicle_yolov3_darknet.tar) | +| Pedestrian Detection | YOLOv3 | 51.8 | [model](https://paddlemodels.bj.bcebos.com/object_detection/pedestrian_yolov3_darknet.tar) | + +## Vehicle Detection + +One of major applications of vehichle detection is traffic monitoring. In this scenary, vehicles to be detected are mostly captured by the cameras mounted on top of traffic light columns. + +### 1. Network + +The network for detecting vehicles is YOLOv3, the backbone of which is Dacknet53. + +### 2. Configuration for training + +PaddleDetection provides users with a configuration file [yolov3_darnet.yml](https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/PaddleDetection/configs/yolov3_darknet.yml) to train YOLOv3 on the COCO dataset, compared with this file, we modify some parameters as followed to conduct the training for vehicle detection: + +* max_iters: 120000 +* num_classes: 6 +* anchors: [[8, 9], [10, 23], [19, 15], [23, 33], [40, 25], [54, 50], [101, 80], [139, 145], [253, 224]] +* label_smooth: false +* nms/nms_top_k: 400 +* nms/score_threshold: 0.005 +* milestones: [60000, 80000] +* dataset_dir: dataset/vehicle + +### 3. Accuracy + +The accuracy of the model trained and evaluated on our private data is shown as followed: + +AP at IoU=.50:.05:.95 is 0.545. + +AP at IoU=.50 is 0.764. + +### 4. Inference + +Users can employ the model to conduct the inference: + +``` +export CUDA_VISIBLE_DEVICES=0 +export PYTHONPATH=$PYTHONPATH:. +python -u tools/infer.py -c contrib/VehicleDetection/vehicle_yolov3_darknet.yml \ + -o weights=https://paddlemodels.bj.bcebos.com/object_detection/vehicle_yolov3_darknet.tar \ + --infer_dir contrib/VehicleDetection/demo \ + --draw_threshold 0.2 \ + --output_dir contrib/VehicleDetection/demo/output + +``` + +Some inference results are visualized below: + +![](VehicleDetection/demo/output/001.jpeg) + +![](VehicleDetection/demo/output/005.png) + +## Pedestrian Detection + +The main applications of pedetestrian detection include intelligent monitoring. In this scenary, photos of pedetestrians are taken by surveillance cameras in public areas, then pedestrian detection are conducted on these photos. + +### 1. Network + +The network for detecting vehicles is YOLOv3, the backbone of which is Dacknet53. + +### 2. Configuration for training + +PaddleDetection provides users with a configuration file [yolov3_darnet.yml](https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/PaddleDetection/configs/yolov3_darknet.yml) to train YOLOv3 on the COCO dataset, compared with this file, we modify some parameters as followed to conduct the training for pedestrian detection: + +* max_iters: 200000 +* num_classes: 1 +* snapshot_iter: 5000 +* milestones: [150000, 180000] +* dataset_dir: dataset/pedestrian + +### 3. Accuracy + +The accuracy of the model trained and evaluted on our private data is shown as followed: + +AP at IoU=.50:.05:.95 is 0.518. + +AP at IoU=.50 is 0.792. + +### 4. Inference + +Users can employ the model to conduct the inference: + +``` +export CUDA_VISIBLE_DEVICES=0 +export PYTHONPATH=$PYTHONPATH:. +python -u tools/infer.py -c contrib/PedestrianDetection/pedestrian_yolov3_darknet.yml \ + -o weights=https://paddlemodels.bj.bcebos.com/object_detection/pedestrian_yolov3_darknet.tar \ + --infer_dir contrib/PedestrianDetection/demo \ + --draw_threshold 0.3 \ + --output_dir contrib/PedestrianDetection/demo/output +``` + +Some inference results are visualized below: + +![](PedestrianDetection/demo/output/001.png) + +![](PedestrianDetection/demo/output/004.png) diff --git a/contrib/README_cn.md b/contrib/README_cn.md new file mode 100644 index 0000000000000000000000000000000000000000..92fc25ece0ca62c0255582208706e8a773f80e5a --- /dev/null +++ b/contrib/README_cn.md @@ -0,0 +1,106 @@ +# PaddleDetection 特色垂类检测模型 + +我们提供了针对不同场景的基于PaddlePaddle的检测模型,用户可以下载模型进行使用。 + +| 任务 | 算法 | 精度(Box AP) | 下载 | +|:---------------------|:---------:|:------:| :---------------------------------------------------------------------------------: | +| 车辆检测 | YOLOv3 | 54.5 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/vehicle_yolov3_darknet.tar) | +| 行人检测 | YOLOv3 | 51.8 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/pedestrian_yolov3_darknet.tar) | + + +## 车辆检测(Vehicle Detection) + +车辆检测的主要应用之一是交通监控。在这样的监控场景中,待检测的车辆多为道路红绿灯柱上的摄像头拍摄所得。 + +### 1. 模型结构 + +Backbone为Dacknet53的YOLOv3。 + +### 2. 训练参数配置 + +PaddleDetection提供了使用COCO数据集对YOLOv3进行训练的参数配置文件[yolov3_darnet.yml](https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/PaddleDetection/configs/yolov3_darknet.yml),与之相比,在进行车辆检测的模型训练时,我们对以下参数进行了修改: + +* max_iters: 120000 +* num_classes: 6 +* anchors: [[8, 9], [10, 23], [19, 15], [23, 33], [40, 25], [54, 50], [101, 80], [139, 145], [253, 224]] +* label_smooth: false +* nms/nms_top_k: 400 +* nms/score_threshold: 0.005 +* milestones: [60000, 80000] +* dataset_dir: dataset/vehicle + +### 3. 精度指标 + +模型在我们内部数据上的精度指标为: + +IOU=.50:.05:.95时的AP为 0.545。 + +IOU=.5时的AP为 0.764。 + +### 4. 预测 + +用户可以使用我们训练好的模型进行车辆检测: + +``` +export CUDA_VISIBLE_DEVICES=0 +export PYTHONPATH=$PYTHONPATH:. +python -u tools/infer.py -c contrib/VehicleDetection/vehicle_yolov3_darknet.yml \ + -o weights=https://paddlemodels.bj.bcebos.com/object_detection/vehicle_yolov3_darknet.tar \ + --infer_dir contrib/VehicleDetection/demo \ + --draw_threshold 0.2 \ + --output_dir contrib/VehicleDetection/demo/output + +``` + +预测结果示例: + +![](VehicleDetection/demo/output/001.jpeg) + +![](VehicleDetection/demo/output/005.png) + +## 行人检测(Pedestrian Detection) + +行人检测的主要应用有智能监控。在监控场景中,大多是从公共区域的监控摄像头视角拍摄行人,获取图像后再进行行人检测。 + +### 1. 模型结构 + +Backbone为Dacknet53的YOLOv3。 + + +### 2. 训练参数配置 + +PaddleDetection提供了使用COCO数据集对YOLOv3进行训练的参数配置文件[yolov3_darnet.yml](https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/PaddleDetection/configs/yolov3_darknet.yml),与之相比,在进行行人检测的模型训练时,我们对以下参数进行了修改: + +* max_iters: 200000 +* num_classes: 1 +* snapshot_iter: 5000 +* milestones: [150000, 180000] +* dataset_dir: dataset/pedestrian + +### 2. 精度指标 + +模型在我们针对监控场景的内部数据上精度指标为: + +IOU=.5时的AP为 0.792。 + +IOU=.5-.95时的AP为 0.518。 + +### 3. 预测 + +用户可以使用我们训练好的模型进行行人检测: + +``` +export CUDA_VISIBLE_DEVICES=0 +export PYTHONPATH=$PYTHONPATH:. +python -u tools/infer.py -c contrib/PedestrianDetection/pedestrian_yolov3_darknet.yml \ + -o weights=https://paddlemodels.bj.bcebos.com/object_detection/pedestrian_yolov3_darknet.tar \ + --infer_dir contrib/PedestrianDetection/demo \ + --draw_threshold 0.3 \ + --output_dir contrib/PedestrianDetection/demo/output +``` + +预测结果示例: + +![](PedestrianDetection/demo/output/001.png) + +![](PedestrianDetection/demo/output/004.png) diff --git a/contrib/VehicleDetection/demo/001.jpeg b/contrib/VehicleDetection/demo/001.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..8786db5eb6773931c363358bb39462b33db55369 Binary files /dev/null and b/contrib/VehicleDetection/demo/001.jpeg differ diff --git a/contrib/VehicleDetection/demo/003.png b/contrib/VehicleDetection/demo/003.png new file mode 100644 index 0000000000000000000000000000000000000000..c01ab4ce769fb3b1c8863093a35d27da0ab10efd Binary files /dev/null and b/contrib/VehicleDetection/demo/003.png differ diff --git a/contrib/VehicleDetection/demo/004.png b/contrib/VehicleDetection/demo/004.png new file mode 100644 index 0000000000000000000000000000000000000000..8907eb8d4d9b82e08ca214509c9fb41ca889db2a Binary files /dev/null and b/contrib/VehicleDetection/demo/004.png differ diff --git a/contrib/VehicleDetection/demo/005.png b/contrib/VehicleDetection/demo/005.png new file mode 100644 index 0000000000000000000000000000000000000000..bf17712809c2fe6fa8e7d4f093ec4ac94523537c Binary files /dev/null and b/contrib/VehicleDetection/demo/005.png differ diff --git a/contrib/VehicleDetection/demo/output/001.jpeg b/contrib/VehicleDetection/demo/output/001.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..aa2b679d4d2a73487edd5f9c67323ab18df93893 Binary files /dev/null and b/contrib/VehicleDetection/demo/output/001.jpeg differ diff --git a/contrib/VehicleDetection/demo/output/005.png b/contrib/VehicleDetection/demo/output/005.png new file mode 100644 index 0000000000000000000000000000000000000000..57f918a30fcc5bf7bda284c1a1a0304e8822d325 Binary files /dev/null and b/contrib/VehicleDetection/demo/output/005.png differ diff --git a/contrib/VehicleDetection/vehicle.json b/contrib/VehicleDetection/vehicle.json new file mode 100644 index 0000000000000000000000000000000000000000..5863a9a8c9e0d8b4daeff31e7fe7869e084d3fb4 --- /dev/null +++ b/contrib/VehicleDetection/vehicle.json @@ -0,0 +1,36 @@ +{ + "images": [], + "annotations": [], + "categories": [ + { + "supercategory": "component", + "id": 1, + "name": "car" + }, + { + "supercategory": "component", + "id": 2, + "name": "truck" + }, + { + "supercategory": "component", + "id": 3, + "name": "bus" + }, + { + "supercategory": "component", + "id": 4, + "name": "motorbike" + }, + { + "supercategory": "component", + "id": 5, + "name": "tricycle" + }, + { + "supercategory": "component", + "id": 6, + "name": "carplate" + } + ] +} diff --git a/contrib/VehicleDetection/vehicle_yolov3_darknet.yml b/contrib/VehicleDetection/vehicle_yolov3_darknet.yml new file mode 100644 index 0000000000000000000000000000000000000000..6a923a0c109a30ed2e247fc7204e81ef0a82eef4 --- /dev/null +++ b/contrib/VehicleDetection/vehicle_yolov3_darknet.yml @@ -0,0 +1,82 @@ +architecture: YOLOv3 +train_feed: YoloTrainFeed +eval_feed: YoloEvalFeed +test_feed: YoloTestFeed +use_gpu: true +max_iters: 120000 +log_smooth_window: 20 +save_dir: output +snapshot_iter: 2000 +metric: COCO +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/DarkNet53_pretrained.tar +weights: https://paddlemodels.bj.bcebos.com/object_detection/vehicle_yolov3_darknet.tar +num_classes: 6 + +YOLOv3: + backbone: DarkNet + yolo_head: YOLOv3Head + +DarkNet: + norm_type: sync_bn + norm_decay: 0. + depth: 53 + +YOLOv3Head: + anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]] + anchors: [[8, 9], [10, 23], [19, 15], + [23, 33], [40, 25], [54, 50], + [101, 80], [139, 145], [253, 224]] + norm_decay: 0. + ignore_thresh: 0.7 + label_smooth: false + nms: + background_label: -1 + keep_top_k: 100 + nms_threshold: 0.45 + nms_top_k: 400 + normalized: false + score_threshold: 0.005 + +LearningRate: + base_lr: 0.001 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: + - 60000 + - 80000 + - !LinearWarmup + start_factor: 0. + steps: 4000 + +OptimizerBuilder: + optimizer: + momentum: 0.9 + type: Momentum + regularizer: + factor: 0.0005 + type: L2 + +YoloTrainFeed: + batch_size: 8 + dataset: + dataset_dir: dataset/vehicle + 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/vehicle + annotation: annotations/instances_val2017.json + image_dir: val2017 + +YoloTestFeed: + batch_size: 1 + image_shape: [3, 608, 608] + dataset: + annotation: contrib/VehicleDetection/vehicle.json diff --git a/dataset/coco/download_coco.py b/dataset/coco/download_coco.py new file mode 100644 index 0000000000000000000000000000000000000000..2b4f7e764e17296ccd8905478bf3ccb3818b909f --- /dev/null +++ b/dataset/coco/download_coco.py @@ -0,0 +1,25 @@ +# 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.path as osp +import logging + +from ppdet.utils.download import download_dataset + +logging.basicConfig(level=logging.INFO) + +download_path = osp.split(osp.realpath(sys.argv[0]))[0] +download_dataset(download_path, 'coco') diff --git a/dataset/fruit/download_fruit.py b/dataset/fruit/download_fruit.py new file mode 100644 index 0000000000000000000000000000000000000000..5cce18895af3eeb81c4e49f4897cc591b2f40f9b --- /dev/null +++ b/dataset/fruit/download_fruit.py @@ -0,0 +1,25 @@ +# 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.path as osp +import logging + +from ppdet.utils.download import download_dataset + +logging.basicConfig(level=logging.INFO) + +download_path = osp.split(osp.realpath(sys.argv[0]))[0] +download_dataset(download_path, 'fruit') diff --git a/dataset/voc/create_list.py b/dataset/voc/create_list.py new file mode 100644 index 0000000000000000000000000000000000000000..1a237493bd50d7be3ee2ab5275159fc5783e5f59 --- /dev/null +++ b/dataset/voc/create_list.py @@ -0,0 +1,25 @@ +# 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.path as osp +import logging + +from ppdet.utils.download import create_voc_list + +logging.basicConfig(level=logging.INFO) + +voc_path = osp.split(osp.realpath(sys.argv[0]))[0] +create_voc_list(voc_path) diff --git a/dataset/voc/download_voc.py b/dataset/voc/download_voc.py new file mode 100644 index 0000000000000000000000000000000000000000..e7f32657f1697bd82f0f7dfbb52a3d1cb987c4bd --- /dev/null +++ b/dataset/voc/download_voc.py @@ -0,0 +1,25 @@ +# 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.path as osp +import logging + +from ppdet.utils.download import download_dataset + +logging.basicConfig(level=logging.INFO) + +download_path = osp.split(osp.realpath(sys.argv[0]))[0] +download_dataset(download_path, 'voc') diff --git a/dataset/voc/label_list.txt b/dataset/voc/label_list.txt new file mode 100644 index 0000000000000000000000000000000000000000..8420ab35ede7400974f25836a6bb543024686a0e --- /dev/null +++ b/dataset/voc/label_list.txt @@ -0,0 +1,20 @@ +aeroplane +bicycle +bird +boat +bottle +bus +car +cat +chair +cow +diningtable +dog +horse +motorbike +person +pottedplant +sheep +sofa +train +tvmonitor diff --git a/dataset/wider_face/download.sh b/dataset/wider_face/download.sh new file mode 100755 index 0000000000000000000000000000000000000000..6c86a22c6826d88846a16fbd43f8b556d8610b8f --- /dev/null +++ b/dataset/wider_face/download.sh @@ -0,0 +1,21 @@ +# All rights `PaddleDetection` reserved +# References: +# @inproceedings{yang2016wider, +# Author = {Yang, Shuo and Luo, Ping and Loy, Chen Change and Tang, Xiaoou}, +# Booktitle = {IEEE Conference on Computer Vision and Pattern Recognition (CVPR)}, +# Title = {WIDER FACE: A Face Detection Benchmark}, +# Year = {2016}} + +DIR="$( cd "$(dirname "$0")" ; pwd -P )" +cd "$DIR" + +# Download the data. +echo "Downloading..." +wget https://dataset.bj.bcebos.com/wider_face/WIDER_train.zip +wget https://dataset.bj.bcebos.com/wider_face/WIDER_val.zip +wget https://dataset.bj.bcebos.com/wider_face/wider_face_split.zip +# Extract the data. +echo "Extracting..." +unzip WIDER_train.zip +unzip WIDER_val.zip +unzip wider_face_split.zip diff --git a/demo/000000014439.jpg b/demo/000000014439.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0abbdab06eb5950b93908cc91adfa640e8a3ac78 Binary files /dev/null and b/demo/000000014439.jpg differ diff --git a/demo/000000014439_640x640.jpg b/demo/000000014439_640x640.jpg new file mode 100644 index 0000000000000000000000000000000000000000..58e9d3e228af43c9b55d8d0cb385ce82ebb8b996 Binary files /dev/null and b/demo/000000014439_640x640.jpg differ diff --git a/demo/000000087038.jpg b/demo/000000087038.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9f77f5d5f057b6f92dc096da704ecb8dee99bdf5 Binary files /dev/null and b/demo/000000087038.jpg differ diff --git a/demo/000000570688.jpg b/demo/000000570688.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cb304bd56c4010c08611a30dcca58ea9140cea54 Binary files /dev/null and b/demo/000000570688.jpg differ diff --git a/demo/cas.png b/demo/cas.png new file mode 100644 index 0000000000000000000000000000000000000000..a60303c99ce0d1ba52e6f89414df8bd5c90fae62 Binary files /dev/null and b/demo/cas.png differ diff --git a/demo/mask_rcnn_demo.ipynb b/demo/mask_rcnn_demo.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..860b185043679e3c7bb28c4fdad505c9f16dda56 --- /dev/null +++ b/demo/mask_rcnn_demo.ipynb @@ -0,0 +1,413 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "ein.tags": "worksheet-0", + "slideshow": { + "slide_type": "-" + } + }, + "source": [ + "Change working directory to the project root" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "autoscroll": false, + "ein.hycell": false, + "ein.tags": "worksheet-0", + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/yang/models/PaddleCV/PaddleDetection\n" + ] + } + ], + "source": [ + "%cd .." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "ein.tags": "worksheet-0", + "slideshow": { + "slide_type": "-" + } + }, + "source": [ + "Now let's take a look at the input image." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "autoscroll": false, + "ein.hycell": false, + "ein.tags": "worksheet-0", + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from PIL import Image\n", + "\n", + "image_path = 'demo/000000570688.jpg'\n", + "img = Image.open(image_path)\n", + "img" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "ein.tags": "worksheet-0", + "slideshow": { + "slide_type": "-" + } + }, + "source": [ + "For inference, preprocess only involves decoding, normalization and transposing to CHW.\n", + "\n", + "**NOTE:** in most cases, one should use the configuration based [data feed](../docs/DATA.md) API which greatly simplifies the data pipeline." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "autoscroll": false, + "ein.hycell": false, + "ein.tags": "worksheet-0", + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "from ppdet.data.transform.operators import DecodeImage, NormalizeImage, Permute\n", + "\n", + "sample = {'im_file': image_path}\n", + "decode = DecodeImage(to_rgb=True)\n", + "normalize = NormalizeImage(\n", + " mean=[0.485, 0.456, 0.406],\n", + " std=[0.229, 0.224, 0.225],\n", + " is_scale=True,\n", + " is_channel_first=False)\n", + "permute = Permute(to_bgr=False, channel_first=True)\n", + "\n", + "sample = permute(normalize(decode(sample)))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "ein.tags": "worksheet-0", + "slideshow": { + "slide_type": "-" + } + }, + "source": [ + "Some extra effort is needed to massage data into the desired format. \n", + "\n", + "**NOTE:** Again, if the data feed API is used, these are handled automatically." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "autoscroll": false, + "ein.hycell": false, + "ein.tags": "worksheet-0", + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "h = sample['h']\n", + "w = sample['w']\n", + "im_info = np.array((h, w, 1), dtype=np.float32)\n", + "\n", + "sample['im_info'] = im_info\n", + "sample['im_shape'] = im_info\n", + "\n", + "# we don't need these\n", + "for key in ['im_file', 'h', 'w']:\n", + " del sample[key]\n", + "\n", + "# batch of a single sample\n", + "sample = {k: v[np.newaxis, ...] for k, v in sample.items()}\n", + "\n", + "feed_var_def = [\n", + " {'name': 'image', 'shape': (h, w, 3)},\n", + " {'name': 'im_info', 'shape': [3]},\n", + " {'name': 'im_shape', 'shape': [3]},\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "ein.tags": "worksheet-0", + "slideshow": { + "slide_type": "-" + } + }, + "source": [ + "Next, build the [Mask R-CNN](https://arxiv.org/abs/1703.06870) model and associated fluid programs" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "autoscroll": false, + "ein.hycell": false, + "ein.tags": "worksheet-0", + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "from paddle import fluid\n", + "from ppdet.modeling import (MaskRCNN, ResNet, ResNetC5, RPNHead, RoIAlign,\n", + " BBoxHead, MaskHead, BBoxAssigner, MaskAssigner)\n", + "\n", + "roi_size = 14\n", + "\n", + "model = MaskRCNN(\n", + " ResNet(feature_maps=4),\n", + " RPNHead(),\n", + " BBoxHead(ResNetC5()),\n", + " BBoxAssigner(),\n", + " RoIAlign(resolution=roi_size),\n", + " MaskAssigner(),\n", + " MaskHead())\n", + "\n", + "startup_prog = fluid.Program()\n", + "infer_prog = fluid.Program()\n", + "with fluid.program_guard(infer_prog, startup_prog):\n", + " with fluid.unique_name.guard():\n", + " feed_vars = {\n", + " var['name']: fluid.layers.data(\n", + " name=var['name'],\n", + " shape=var['shape'],\n", + " dtype='float32',\n", + " lod_level=0) for var in feed_var_def\n", + " }\n", + " test_fetches = model.test(feed_vars)\n", + "infer_prog = infer_prog.clone(for_test=True)\n", + "\n", + "# use GPU if available\n", + "if fluid.core.get_cuda_device_count() > 0:\n", + " place = fluid.CUDAPlace(0)\n", + "else:\n", + " place = fluid.CPUPlace()\n", + "\n", + "exe = fluid.Executor(place)\n", + "_ = exe.run(startup_prog)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "ein.tags": "worksheet-0", + "slideshow": { + "slide_type": "-" + } + }, + "source": [ + "Load the checkpoint weights, just wait a couple of minutes for it to be downloaded." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "autoscroll": false, + "ein.hycell": false, + "ein.tags": "worksheet-0", + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 140690/140690 [00:12<00:00, 10843.70KB/s]\n" + ] + } + ], + "source": [ + "from ppdet.utils import checkpoint\n", + "\n", + "ckpt_url = 'https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r50_1x.tar'\n", + "checkpoint.load_checkpoint(exe, infer_prog, ckpt_url)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "ein.tags": "worksheet-0", + "slideshow": { + "slide_type": "-" + } + }, + "source": [ + "Run the program and fetch the result. " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "autoscroll": false, + "ein.hycell": false, + "ein.tags": "worksheet-0", + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "output = exe.run(infer_prog, feed=sample,\n", + " fetch_list=[t.name for t in test_fetches.values()],\n", + " return_numpy=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "ein.tags": "worksheet-0", + "slideshow": { + "slide_type": "-" + } + }, + "source": [ + "Again, we need to massage the result a bit for visualization." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "autoscroll": false, + "ein.hycell": false, + "ein.tags": "worksheet-0", + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "res = {\n", + " k: (np.array(v), v.recursive_sequence_lengths())\n", + " for k, v in zip(test_fetches.keys(), output)\n", + "}\n", + "# fake image id\n", + "res['im_id'] = [[[0] for _ in range(res['bbox'][1][0][0])]]\n", + "res['im_shape'] = [[im_info]]" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "ein.tags": "worksheet-0", + "slideshow": { + "slide_type": "-" + } + }, + "source": [ + "Now overlay the bboxes and masks onto the image...\n", + "\n", + "And voila, we've successully built and run the Mask R-CNN inference pipeline." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "autoscroll": false, + "ein.hycell": false, + "ein.tags": "worksheet-0", + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from ppdet.utils.coco_eval import bbox2out, mask2out, coco17_category_info\n", + "from ppdet.utils.visualizer import visualize_results\n", + "\n", + "cls2cat, cat2name = coco17_category_info()\n", + "bboxes = bbox2out([res], cls2cat)\n", + "masks = mask2out([res], cls2cat, roi_size)\n", + "\n", + "visualize_results(img, 0, cat2name, 0.5, bboxes, masks)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.16" + }, + "name": "mask_rcnn_demo.ipynb" + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/demo/obj365_gt.png b/demo/obj365_gt.png new file mode 100644 index 0000000000000000000000000000000000000000..eb69077f6ba1676d9fc0ba3e4cada645c5ab7245 Binary files /dev/null and b/demo/obj365_gt.png differ diff --git a/demo/obj365_pred.png b/demo/obj365_pred.png new file mode 100644 index 0000000000000000000000000000000000000000..d99fb1fd198f1f8b6e003741375e9aa5524f117a Binary files /dev/null and b/demo/obj365_pred.png differ diff --git a/demo/orange_71.jpg b/demo/orange_71.jpg new file mode 100644 index 0000000000000000000000000000000000000000..da7974a1a1371298f1ca5f4ef9c82bd3824d7ac3 Binary files /dev/null and b/demo/orange_71.jpg differ diff --git a/demo/orange_71_detection.jpg b/demo/orange_71_detection.jpg new file mode 100644 index 0000000000000000000000000000000000000000..88cbf9c97120f79d5ef5f80ccb789a6e3c29bedf Binary files /dev/null and b/demo/orange_71_detection.jpg differ diff --git a/demo/output/000000570688.jpg b/demo/output/000000570688.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8a0f84f38331093a0e1afc52d4b7747535bdbb6d Binary files /dev/null and b/demo/output/000000570688.jpg differ diff --git a/demo/output/12_Group_Group_12_Group_Group_12_935.jpg b/demo/output/12_Group_Group_12_Group_Group_12_935.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2a563361ae03fbe079dba017374eee51ccbd17dd Binary files /dev/null and b/demo/output/12_Group_Group_12_Group_Group_12_935.jpg differ diff --git a/demo/tensorboard_fruit.jpg b/demo/tensorboard_fruit.jpg new file mode 100644 index 0000000000000000000000000000000000000000..44a955fafffb4ab03d911818e20e6f72499f2f4f Binary files /dev/null and b/demo/tensorboard_fruit.jpg differ diff --git a/docs/BENCHMARK_INFER_cn.md b/docs/BENCHMARK_INFER_cn.md new file mode 100644 index 0000000000000000000000000000000000000000..bc4c25fee1555eb124c25edfcd066af0f6c7bfcf --- /dev/null +++ b/docs/BENCHMARK_INFER_cn.md @@ -0,0 +1,89 @@ + + +# 推理Benchmark + + + +- 测试环境: + - CUDA 9.0 + - CUDNN 7.5 + - TensorRT-5.1.2.2 + - PaddlePaddle v1.6 + - GPU分别为: Tesla V100和Tesla P4 +- 测试方式: + - 为了方面比较不同模型的推理速度,输入采用同样大小的图片,为 3x640x640,采用 `demo/000000014439_640x640.jpg` 图片。 + - Batch Size=1 + - 去掉前10轮warmup时间,测试100轮的平均时间,单位ms/image,包括输入数据拷贝至GPU的时间、计算时间、数据拷贝只CPU的时间。 + - 采用Fluid C++预测引擎: 包含Fluid C++预测、Fluid-TensorRT预测,下面同时测试了Float32 (FP32) 和Float16 (FP16)的推理速度。 + - 测试时开启了 FLAGS_cudnn_exhaustive_search=True,使用exhaustive方式搜索卷积计算算法。 + +### 推理速度 + + + + + +| 模型 | Tesla V100 Fluid (ms/image) | Tesla V100 Fluid-TensorRT-FP32 (ms/image) | Tesla V100 Fluid-TensorRT-FP16 (ms/image) | Tesla P4 Fluid (ms/image) | Tesla P4 Fluid-TensorRT-FP32 (ms/image) | +| ------------------------------------- | ----------------------------- | ------------------------------------------- | ------------------------------------------- | --------------------------- | ----------------------------------------- | +| faster_rcnn_r50_1x | 147.488 | 146.124 | 142.416 | 471.547 | 471.631 | +| faster_rcnn_r50_2x | 147.636 | 147.73 | 141.664 | 471.548 | 472.86 | +| faster_rcnn_r50_vd_1x | 146.588 | 144.767 | 141.208 | 459.357 | 457.852 | +| faster_rcnn_r50_fpn_1x | 25.11 | 24.758 | 20.744 | 59.411 | 57.585 | +| faster_rcnn_r50_fpn_2x | 25.351 | 24.505 | 20.509 | 59.594 | 57.591 | +| faster_rcnn_r50_vd_fpn_2x | 25.514 | 25.292 | 21.097 | 61.026 | 58.377 | +| faster_rcnn_r50_fpn_gn_2x | 36.959 | 36.173 | 32.356 | 101.339 | 101.212 | +| faster_rcnn_dcn_r50_fpn_1x | 28.707 | 28.162 | 27.503 | 68.154 | 67.443 | +| faster_rcnn_dcn_r50_vd_fpn_2x | 28.576 | 28.271 | 27.512 | 68.959 | 68.448 | +| faster_rcnn_r101_1x | 153.267 | 150.985 | 144.849 | 490.104 | 486.836 | +| faster_rcnn_r101_fpn_1x | 30.949 | 30.331 | 24.021 | 73.591 | 69.736 | +| faster_rcnn_r101_fpn_2x | 30.918 | 29.126 | 23.677 | 73.563 | 70.32 | +| faster_rcnn_r101_vd_fpn_1x | 31.144 | 30.202 | 23.57 | 74.767 | 70.773 | +| faster_rcnn_r101_vd_fpn_2x | 30.678 | 29.969 | 23.327 | 74.882 | 70.842 | +| faster_rcnn_x101_vd_64x4d_fpn_1x | 60.36 | 58.461 | 45.172 | 132.178 | 131.734 | +| faster_rcnn_x101_vd_64x4d_fpn_2x | 59.003 | 59.163 | 46.065 | 131.422 | 132.186 | +| faster_rcnn_dcn_r101_vd_fpn_1x | 36.862 | 37.205 | 36.539 | 93.273 | 92.616 | +| faster_rcnn_dcn_x101_vd_64x4d_fpn_1x | 78.476 | 78.335 | 77.559 | 185.976 | 185.996 | +| faster_rcnn_se154_vd_fpn_s1x | 166.282 | 90.508 | 80.738 | 304.653 | 193.234 | +| mask_rcnn_r50_1x | 160.185 | 160.4 | 160.322 | - | - | +| mask_rcnn_r50_2x | 159.821 | 159.527 | 160.41 | - | - | +| mask_rcnn_r50_fpn_1x | 95.72 | 95.719 | 92.455 | 259.8 | 258.04 | +| mask_rcnn_r50_fpn_2x | 84.545 | 83.567 | 79.269 | 227.284 | 222.975 | +| mask_rcnn_r50_vd_fpn_2x | 82.07 | 82.442 | 77.187 | 223.75 | 221.683 | +| mask_rcnn_r50_fpn_gn_2x | 94.936 | 94.611 | 91.42 | 265.468 | 263.76 | +| mask_rcnn_dcn_r50_fpn_1x | 97.828 | 97.433 | 93.76 | 256.295 | 258.056 | +| mask_rcnn_dcn_r50_vd_fpn_2x | 77.831 | 79.453 | 76.983 | 205.469 | 204.499 | +| mask_rcnn_r101_fpn_1x | 95.543 | 97.929 | 90.314 | 252.997 | 250.782 | +| mask_rcnn_r101_vd_fpn_1x | 98.046 | 97.647 | 90.272 | 261.286 | 262.108 | +| mask_rcnn_x101_vd_64x4d_fpn_1x | 115.461 | 115.756 | 102.04 | 296.066 | 293.62 | +| mask_rcnn_x101_vd_64x4d_fpn_2x | 107.144 | 107.29 | 97.275 | 267.636 | 267.577 | +| mask_rcnn_dcn_r101_vd_fpn_1x | 85.504 | 84.875 | 84.907 | 225.202 | 226.585 | +| mask_rcnn_dcn_x101_vd_64x4d_fpn_1x | 129.937 | 129.934 | 127.804 | 326.786 | 326.161 | +| mask_rcnn_se154_vd_fpn_s1x | 214.188 | 139.807 | 121.516 | 440.391 | 439.727 | +| cascade_rcnn_r50_fpn_1x | 36.866 | 36.949 | 36.637 | 101.851 | 101.912 | +| cascade_mask_rcnn_r50_fpn_1x | 110.344 | 106.412 | 100.367 | 301.703 | 297.739 | +| cascade_rcnn_dcn_r50_fpn_1x | 40.412 | 39.58 | 39.853 | 110.346 | 110.077 | +| cascade_mask_rcnn_r50_fpn_gn_2x | 170.092 | 168.758 | 163.298 | 527.998 | 529.59 | +| cascade_rcnn_dcn_r101_vd_fpn_1x | 48.414 | 48.849 | 48.701 | 134.9 | 134.846 | +| cascade_rcnn_dcn_x101_vd_64x4d_fpn_1x | 90.062 | 90.218 | 90.009 | 228.67 | 228.396 | +| retinanet_r101_fpn_1x | 55.59 | 54.636 | 48.489 | 90.394 | 83.951 | +| retinanet_r50_fpn_1x | 50.048 | 47.932 | 44.385 | 73.819 | 70.282 | +| retinanet_x101_vd_64x4d_fpn_1x | 83.329 | 83.446 | 70.76 | 145.936 | 146.168 | +| yolov3_darknet | 21.427 | 20.252 | 13.856 | 55.173 | 55.692 | +| yolov3_darknet_voc | 17.58 | 16.241 | 9.473 | 51.049 | 51.249 | +| yolov3_mobilenet_v1 | 12.869 | 11.834 | 9.408 | 24.887 | 21.352 | +| yolov3_mobilenet_v1_voc | 9.118 | 8.146 | 5.575 | 20.787 | 17.169 | +| yolov3_r34 | 14.914 | 14.125 | 11.176 | 20.798 | 20.822 | +| yolov3_r34_voc | 11.288 | 10.73 | 7.7 | 25.874 | 22.399 | +| ssd_mobilenet_v1_voc | 5.763 | 5.854 | 4.589 | 11.75 | 9.485 | +| ssd_vgg16_300 | 28.722 | 29.644 | 20.399 | 73.707 | 74.531 | +| ssd_vgg16_300_voc | 18.425 | 19.288 | 11.298 | 56.297 | 56.201 | +| ssd_vgg16_512 | 27.471 | 28.328 | 19.328 | 68.685 | 69.808 | +| ssd_vgg16_512_voc | 18.721 | 19.636 | 12.004 | 54.688 | 56.174 | + +1. RCNN系列模型Fluid-TensorRT速度相比Fluid预测没有优势,原因是: TensorRT仅支持定长输入,当前基于ResNet系列的RCNN模型,只有backbone部分采用了TensorRT子图计算,比较耗时的stage-5没有基于TensorRT计算。 Fluid对CNN模型也做了一系列的融合优化。后续TensorRT版本升级、或有其他优化策略时再更新数据。 +2. YOLO v3系列模型,Fluid-TensorRT相比Fluid预测加速5% - 10%不等。 +3. SSD和YOLOv3系列模型 TensorRT-FP16预测速度有一定的优势,加速约20% - 40%不等。具体如下图。 + +
+ +
diff --git a/docs/CACascadeRCNN.md b/docs/CACascadeRCNN.md new file mode 100644 index 0000000000000000000000000000000000000000..8e72b9af30183abf6c028ec2f4ee2ec695a29a6c --- /dev/null +++ b/docs/CACascadeRCNN.md @@ -0,0 +1,51 @@ +# CACascade RCNN +## 简介 +CACascade RCNN是百度视觉技术部在Objects365 2019 Challenge上夺冠的最佳单模型之一,Objects365是在通用物体检测领域的一个全新的数据集,旨在促进对自然场景不同对象的检测研究。Objects365在63万张图像上标注了365个对象类,训练集中共有超过1000万个边界框。这里放出的是Full Track任务中最好的单模型之一。 + +
+ +
+ +## 方法描述 + +针对大规模物体检测算法的特点,我们提出了一种基于图片包含物体类别的数量的采样方式(Class Aware Sampling)。基于这种方式进行训练模型可以在更短的时间使模型收敛到更好的效果。 + +
+ +
+ +本次公布的最好单模型是一个基于Cascade RCNN的两阶段检测模型,在此基础上将Backbone替换为更加强大的SENet154模型,Deformable Conv模块以及更复杂二阶段网络结构,针对BatchSize比较小的情况增加了Group Normalization操作并同时使用了多尺度训练,最终达到了非常理想的效果。预训练模型先后分别在ImageNet和COCO数据集上进行了训练,其中在COCO数据集上训练时增加了Mask分支,其余结构与CACascade RCNN相同, 会在启动训练时自动下载。 + +## 使用方法 + +1.准备数据 + +数据需要通过[Objects365官方网站](https://www.objects365.org/download.html)进行申请下载,数据下载后将数据放置在dataset目录中。 +``` +${THIS REPO ROOT} + \--dataset + \-- objects365 + \-- annotations + |-- train.json + |-- val.json + \-- train + \-- val +``` + +2.启动训练模型 + +```bash +python tools/train.py -c configs/obj365/cascade_rcnn_dcnv2_se154_vd_fpn_gn.yml +``` + +3.模型预测结果 + +| 模型 | 验证集 mAP | 下载链接 | +| :-----------------: | :--------: | :----------------------------------------------------------: | +| CACascadeRCNN SE154 | 31.7 | [model](https://paddlemodels.bj.bcebos.com/object_detection/cascade_rcnn_dcnv2_se154_vd_fpn_gn_cas_obj365.tar) | + +## 模型效果 + +
+ +
diff --git a/docs/CONFIG.md b/docs/CONFIG.md new file mode 100644 index 0000000000000000000000000000000000000000..3cba54eb546cfb648cc7b5bd2e135652a040b309 --- /dev/null +++ b/docs/CONFIG.md @@ -0,0 +1,206 @@ +English | [简体中文](CONFIG_cn.md) + +# Config Pipline + +## Introduction + +PaddleDetection takes a rather principled approach to configuration management. We aim to automate the configuration workflow and to reduce configuration errors. + + +## Rationale + +Presently, configuration in mainstream frameworks are usually dictionary based: the global config is simply a giant, loosely defined Python dictionary. + +This approach is error prone, e.g., misspelled or displaced keys may lead to serious errors in training process, causing time loss and wasted resources. + +To avoid the common pitfalls, with automation and static analysis in mind, we propose a configuration design that is user friendly, easy to maintain and extensible. + + +## Design + +The design utilizes some of Python's reflection mechanism to extract configuration schematics from Python class definitions. + +To be specific, it extracts information from class constructor arguments, including names, docstrings, default values, data types (if type hints are available). + +This approach advocates modular and testable design, leading to a unified and extensible code base. + + +### API + +Most of the functionality is exposed in `ppdet.core.workspace` module. + +- `register`: This decorator register a class as configurable module; it understands several special annotations in the class definition. + - `__category__`: For better organization, modules are classified into categories. + - `__inject__`: A list of constructor arguments, which are intended to take module instances as input, module instances will be created at runtime an injected. The corresponding configuration value can be a class name string, a serialized object, a config key pointing to a serialized object, or a dict (in which case the constructor needs to handle it, see example below). + - `__op__`: Shortcut for wrapping PaddlePaddle operators into a callable objects, together with `__append_doc__` (extracting docstring from target PaddlePaddle operator automatically), this can be a real time saver. +- `serializable`: This decorator make a class directly serializable in yaml config file, by taking advantage of [pyyaml](https://pyyaml.org/wiki/PyYAMLDocumentation)'s serialization mechanism. +- `create`: Constructs a module instance according to global configuration. +- `load_config` and `merge_config`: Loading yaml file and merge config settings from command line. + + +### Example + +Take the `RPNHead` module for example, it is composed of several PaddlePaddle operators. We first wrap those operators into classes, then pass in instances of these classes when instantiating the `RPNHead` module. + +```python +# excerpt from `ppdet/modeling/ops.py` +from ppdet.core.workspace import register, serializable + +# ... more operators + +@register +@serializable +class GenerateProposals(object): + # NOTE this class simply wraps a PaddlePaddle operator + __op__ = fluid.layers.generate_proposals + # NOTE docstring for args are extracted from PaddlePaddle OP + __append_doc__ = True + + def __init__(self, + pre_nms_top_n=6000, + post_nms_top_n=1000, + nms_thresh=.5, + min_size=.1, + eta=1.): + super(GenerateProposals, self).__init__() + self.pre_nms_top_n = pre_nms_top_n + self.post_nms_top_n = post_nms_top_n + self.nms_thresh = nms_thresh + self.min_size = min_size + self.eta = eta + +# ... more operators + +# excerpt from `ppdet/modeling/anchor_heads/rpn_head.py` +from ppdet.core.workspace import register +from ppdet.modeling.ops import AnchorGenerator, RPNTargetAssign, GenerateProposals + +@register +class RPNHead(object): + """ + RPN Head + + Args: + anchor_generator (object): `AnchorGenerator` instance + rpn_target_assign (object): `RPNTargetAssign` instance + train_proposal (object): `GenerateProposals` instance for training + test_proposal (object): `GenerateProposals` instance for testing + """ + __inject__ = [ + 'anchor_generator', 'rpn_target_assign', 'train_proposal', + 'test_proposal' + ] + + def __init__(self, + anchor_generator=AnchorGenerator().__dict__, + rpn_target_assign=RPNTargetAssign().__dict__, + train_proposal=GenerateProposals(12000, 2000).__dict__, + test_proposal=GenerateProposals().__dict__): + super(RPNHead, self).__init__() + self.anchor_generator = anchor_generator + self.rpn_target_assign = rpn_target_assign + self.train_proposal = train_proposal + self.test_proposal = test_proposal + if isinstance(anchor_generator, dict): + self.anchor_generator = AnchorGenerator(**anchor_generator) + if isinstance(rpn_target_assign, dict): + self.rpn_target_assign = RPNTargetAssign(**rpn_target_assign) + if isinstance(train_proposal, dict): + self.train_proposal = GenerateProposals(**train_proposal) + if isinstance(test_proposal, dict): + self.test_proposal = GenerateProposals(**test_proposal) +``` + +The corresponding(generated) YAML snippet is as follows, note this is the configuration in **FULL**, all the default values can be omitted. In case of the above example, all arguments have default value, meaning nothing is required in the config file. + +```yaml +RPNHead: + test_proposal: + eta: 1.0 + min_size: 0.1 + nms_thresh: 0.5 + post_nms_top_n: 1000 + pre_nms_top_n: 6000 + train_proposal: + eta: 1.0 + min_size: 0.1 + nms_thresh: 0.5 + post_nms_top_n: 2000 + pre_nms_top_n: 12000 + anchor_generator: + # ... + rpn_target_assign: + # ... +``` + +Example snippet that make use of the `RPNHead` module. + +```python +from ppdet.core.workspace import load_config, merge_config, create + +load_config('some_config_file.yml') +merge_config(more_config_options_from_command_line) + +rpn_head = create('RPNHead') +# ... code that use the created module! +``` + +Configuration file can also have serialized objects in it, denoted with `!`, for example + +```yaml +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [60000, 80000] + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 +``` + +[Complete config files](config_example/) of multiple detection architectures are given and brief description of each parameter. + +## Requirements + +Two Python packages are used, both are optional. + +- [typeguard](https://github.com/agronholm/typeguard) is used for type checking in Python 3. +- [docstring\_parser](https://github.com/rr-/docstring_parser) is needed for docstring parsing. + +To install them, simply run: + +```shell +pip install typeguard http://github.com/willthefrog/docstring_parser/tarball/master +``` + + +## Tooling + +A small utility (`tools/configure.py`) is included to simplify the configuration process, it provides 4 commands to walk users through the configuration process: + +1. `list`: List currently registered modules by category, one can also specify which category to list with the `--category` flag. +2. `help`: Get help information for a module, including description, options, configuration template and example command line flags. +3. `analyze`: Check configuration file for missing/extraneous options, options with mismatch type (if type hint is given) and missing dependencies, it also highlights user provided values (overridden default values). +4. `generate`: Generate a configuration template for a given list of modules. By default it generates a complete configuration file, which can be quite verbose; if a `--minimal` flag is given, it generates a template that only contain non optional settings. For example, to generate a configuration for Faster R-CNN architecture with `ResNet` backbone and `FPN`, run: + + ```shell + python tools/configure.py generate FasterRCNN ResNet RPNHead RoIAlign BBoxAssigner BBoxHead FasterRCNNTrainFeed FasterRCNNTestFeed LearningRate OptimizerBuilder + ``` + + For a minimal version, run: + + ```shell + python tools/configure.py --minimal generate FasterRCNN BBoxHead + ``` + + +## FAQ + +**Q:** There are some configuration options that are used by multiple modules (e.g., `num_classes`), how do I avoid duplication in config files? + +**A:** We provided a `__shared__` annotation for exactly this purpose, simply annotate like this `__shared__ = ['num_classes']`. It works as follows: + +1. if `num_classes` is configured for a module in config file, it takes precedence. +2. if `num_classes` is not configured for a module but is present in the config file as a global key, its value will be used. +3. otherwise, the default value (`81`) will be used. diff --git a/docs/CONFIG_cn.md b/docs/CONFIG_cn.md new file mode 100644 index 0000000000000000000000000000000000000000..8b7eaa653a65264db189fa88a125ce10b5a6f667 --- /dev/null +++ b/docs/CONFIG_cn.md @@ -0,0 +1,196 @@ +# 配置模块 + +## 简介 + +为了使配置过程更加自动化并减少配置错误,PaddleDetection的配置管理采取了较为严谨的设计。 + + +## 设计思想 + +目前主流框架全局配置基本是一个Python dict,这种设计对配置的检查并不严格,拼写错误或者遗漏的配置项往往会造成训练过程中的严重错误,进而造成时间及资源的浪费。为了避免这些陷阱,从自动化和静态分析的原则出发,PaddleDetection采用了一种用户友好、 易于维护和扩展的配置设计。 + + +## 基本设计 + +利用Python的反射机制,PaddleDection的配置系统从Python类的构造函数抽取多种信息 - 如参数名、初始值、参数注释、数据类型(如果给出type hint)- 来作为配置规则。 这种设计便于设计的模块化,提升可测试性及扩展性。 + + +### API + +配置系统的大多数功能由 `ppdet.core.workspace` 模块提供 + +- `register`: 装饰器,将类注册为可配置模块;能够识别类定义中的一些特殊标注。 + - `__category__`: 为便于组织,模块可以分为不同类别。 + - `__inject__`: 如果模块由多个子模块组成,可以这些子模块实例作为构造函数的参数注入。对应的默认值及配置项可以是类名字符串,yaml序列化的对象,指向序列化对象的配置键值或者Python dict(构造函数需要对其作出处理,参见下面的例子)。 + - `__op__`: 配合 `__append_doc__` (抽取目标OP的 注释)使用,可以方便快速的封装PaddlePaddle底层OP。 +- `serializable`: 装饰器,利用 [pyyaml](https://pyyaml.org/wiki/PyYAMLDocumentation) 的序列化机制,可以直接将一个类实例序列化及反序列化。 +- `create`: 根据全局配置构造一个模块实例。 +- `load_config` and `merge_config`: 加载yaml文件,合并命令行提供的配置项。 + + +### 示例 + +以 `RPNHead` 模块为例,该模块包含多个PaddlePaddle OP,先将这些OP封装成类,并将其实例在构造 `RPNHead` 时注入。 + +```python +# excerpt from `ppdet/modeling/ops.py` +from ppdet.core.workspace import register, serializable + +# ... more operators + +@register +@serializable +class GenerateProposals(object): + # NOTE this class simply wraps a PaddlePaddle operator + __op__ = fluid.layers.generate_proposals + # NOTE docstring for args are extracted from PaddlePaddle OP + __append_doc__ = True + + def __init__(self, + pre_nms_top_n=6000, + post_nms_top_n=1000, + nms_thresh=.5, + min_size=.1, + eta=1.): + super(GenerateProposals, self).__init__() + self.pre_nms_top_n = pre_nms_top_n + self.post_nms_top_n = post_nms_top_n + self.nms_thresh = nms_thresh + self.min_size = min_size + self.eta = eta + +# ... more operators + +# excerpt from `ppdet/modeling/anchor_heads/rpn_head.py` +from ppdet.core.workspace import register +from ppdet.modeling.ops import AnchorGenerator, RPNTargetAssign, GenerateProposals + +@register +class RPNHead(object): + """ + RPN Head + + Args: + anchor_generator (object): `AnchorGenerator` instance + rpn_target_assign (object): `RPNTargetAssign` instance + train_proposal (object): `GenerateProposals` instance for training + test_proposal (object): `GenerateProposals` instance for testing + """ + __inject__ = [ + 'anchor_generator', 'rpn_target_assign', 'train_proposal', + 'test_proposal' + ] + + def __init__(self, + anchor_generator=AnchorGenerator().__dict__, + rpn_target_assign=RPNTargetAssign().__dict__, + train_proposal=GenerateProposals(12000, 2000).__dict__, + test_proposal=GenerateProposals().__dict__): + super(RPNHead, self).__init__() + self.anchor_generator = anchor_generator + self.rpn_target_assign = rpn_target_assign + self.train_proposal = train_proposal + self.test_proposal = test_proposal + if isinstance(anchor_generator, dict): + self.anchor_generator = AnchorGenerator(**anchor_generator) + if isinstance(rpn_target_assign, dict): + self.rpn_target_assign = RPNTargetAssign(**rpn_target_assign) + if isinstance(train_proposal, dict): + self.train_proposal = GenerateProposals(**train_proposal) + if isinstance(test_proposal, dict): + self.test_proposal = GenerateProposals(**test_proposal) +``` + +对应的yaml配置如下,请注意这里给出的是 **完整** 配置,其中所有默认值配置项都可以省略。上面的例子中的模块所有的构造函数参数都提供了默认值,因此配置文件中可以完全略过其配置。 + +```yaml +RPNHead: + test_proposal: + eta: 1.0 + min_size: 0.1 + nms_thresh: 0.5 + post_nms_top_n: 1000 + pre_nms_top_n: 6000 + train_proposal: + eta: 1.0 + min_size: 0.1 + nms_thresh: 0.5 + post_nms_top_n: 2000 + pre_nms_top_n: 12000 + anchor_generator: + # ... + rpn_target_assign: + # ... +``` + +`RPNHead` 模块实际使用代码示例。 + +```python +from ppdet.core.workspace import load_config, merge_config, create + +load_config('some_config_file.yml') +merge_config(more_config_options_from_command_line) + +rpn_head = create('RPNHead') +# ... code that use the created module! +``` + +配置文件用可以直接序列化模块实例,用 `!` 标示,如 + +```yaml +LearningRate: + base_lr: 0.01 + schedulers: + - !PiecewiseDecay + gamma: 0.1 + milestones: [60000, 80000] + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 +``` + +[示例配置文件](config_example/)中给出了多种检测结构的完整配置文件,以及其中各个超参的简要说明。 + +## 安装依赖 + +配置系统用到两个Python包,均为可选安装。 + +- [typeguard](https://github.com/agronholm/typeguard) 在Python 3中用来进行数据类型验证。 +- [docstring\_parser](https://github.com/rr-/docstring_parser) 用来解析注释。 + +如需安装,运行下面命令即可。 + +```shell +pip install typeguard http://github.com/willthefrog/docstring_parser/tarball/master +``` + + +## 相关工具 + +为了方便用户配置,PaddleDection提供了一个工具 (`tools/configure.py`), 共支持四个子命令: + +1. `list`: 列出当前已注册的模块,如需列出具体类别的模块,可以使用 `--category` 指定。 +2. `help`: 显示指定模块的帮助信息,如描述,配置项,配置文件模板及命令行示例。 +3. `analyze`: 检查配置文件中的缺少或者多余的配置项以及依赖缺失,如果给出type hint, 还可以检查配置项中错误的数据类型。非默认配置也会高亮显示。 +4. `generate`: 根据给出的模块列表生成配置文件,默认生成完整配置,如果指定 `--minimal` ,生成最小配置,即省略所有默认配置项。例如,执行下列命令可以生成Faster R-CNN (`ResNet` backbone + `FPN`) 架构的配置文件: + + ```shell + python tools/configure.py generate FasterRCNN ResNet RPNHead RoIAlign BBoxAssigner BBoxHead FasterRCNNTrainFeed FasterRCNNTestFeed LearningRate OptimizerBuilder + ``` + + 如需最小配置,运行: + + ```shell + python tools/configure.py --minimal generate FasterRCNN BBoxHead + ``` + + +## FAQ + +**Q:** 某些配置项会在多个模块中用到(如 `num_classes`),如何避免在配置文件中多次重复设置? + +**A:** 框架提供了 `__shared__` 标记来实现配置的共享,用户可以标记参数,如 `__shared__ = ['num_classes']` ,配置数值作用规则如下: + +1. 如果模块配置中提供了 `num_classes` ,会优先使用其数值。 +2. 如果模块配置中未提供 `num_classes` ,但配置文件中存在全局键值,那么会使用全局键值。 +3. 两者均为配置的情况下,将使用默认值(`81`)。 diff --git a/docs/DATA.md b/docs/DATA.md new file mode 100644 index 0000000000000000000000000000000000000000..466405022c2a6d076844ffb73ebd74565240ca55 --- /dev/null +++ b/docs/DATA.md @@ -0,0 +1,235 @@ +English | [简体中文](DATA_cn.md) + +# Data Pipline + +## Introduction + +The data pipeline is responsible for loading and converting data. Each +resulting data sample is a tuple of np.ndarrays. +For example, Faster R-CNN training uses samples of this format: `[(im, +im_info, im_id, gt_bbox, gt_class, is_crowd), (...)]`. + +### Implementation + +The data pipeline consists of four sub-systems: data parsing, image +pre-processing, data conversion and data feeding APIs. + +Data samples are collected to form `data.Dataset`s, usually 3 sets are +needed for training, validation, and testing respectively. + +First, `data.source` loads the data files into memory, then +`data.transform` processes them, and lastly, the batched samples +are fetched by `data.Reader`. + +Sub-systems details: +1. Data parsing +Parses various data sources and creates `data.Dataset` instances. Currently, +following data sources are supported: + +- COCO data source + +Loads `COCO` type datasets with directory structures like this: + + ``` + dataset/coco/ + ├── annotations + │ ├── instances_train2014.json + │ ├── instances_train2017.json + │ ├── instances_val2014.json + │ ├── instances_val2017.json + │ | ... + ├── train2017 + │ ├── 000000000009.jpg + │ ├── 000000580008.jpg + │ | ... + ├── val2017 + │ ├── 000000000139.jpg + │ ├── 000000000285.jpg + │ | ... + | ... + ``` + +- Pascal VOC data source + +Loads `Pascal VOC` like datasets with directory structure like this: + + ``` + dataset/voc/ + ├── train.txt + ├── val.txt + ├── test.txt + ├── label_list.txt (optional) + ├── VOCdevkit/VOC2007 + │ ├── Annotations + │ ├── 001789.xml + │ | ... + │ ├── JPEGImages + │ ├── 001789.xml + │ | ... + │ ├── ImageSets + │ | ... + ├── VOCdevkit/VOC2012 + │ ├── Annotations + │ ├── 003876.xml + │ | ... + │ ├── JPEGImages + │ ├── 003876.xml + │ | ... + │ ├── ImageSets + │ | ... + | ... + ``` + +**NOTE:** If you set `use_default_label=False` in yaml configs, the `label_list.txt` +of Pascal VOC dataset will be read, otherwise, `label_list.txt` is unnecessary and +the default Pascal VOC label list which defined in +[voc\_loader.py](../ppdet/data/source/voc_loader.py) will be used. + +- Roidb data source +A generalized data source serialized as pickle files, which have the following +structure: +```python +(records, cname2id) +# `cname2id` is a `dict` which maps category name to class IDs +# and `records` is a list of dict of this structure: +{ + 'im_file': im_fname, # image file name + 'im_id': im_id, # image ID + 'h': im_h, # height of image + 'w': im_w, # width of image + 'is_crowd': is_crowd, # crowd marker + 'gt_class': gt_class, # ground truth class + 'gt_bbox': gt_bbox, # ground truth bounding box + 'gt_poly': gt_poly, # ground truth segmentation +} +``` + +We provide a tool to generate roidb data sources. To convert `COCO` or `VOC` +like dataset, run this command: +```sh +# --type: the type of original data (xml or json) +# --annotation: the path of file, which contains the name of annotation files +# --save-dir: the save path +# --samples: the number of samples (default is -1, which mean all datas in dataset) +python ./ppdet/data/tools/generate_data_for_training.py + --type=json \ + --annotation=./annotations/instances_val2017.json \ + --save-dir=./roidb \ + --samples=-1 +``` + + 2. Image preprocessing +the `data.transform.operator` module provides operations such as image +decoding, expanding, cropping, etc. Multiple operators are combined to form +larger processing pipelines. + + 3. Data transformer +Transform a `data.Dataset` to achieve various desired effects, Notably: the +`data.transform.paralle_map` transformer accelerates image processing with +multi-threads or multi-processes. More transformers can be found in +`data.transform.transformer`. + + 4. Data feeding apis +To facilitate data pipeline building, we combine multiple `data.Dataset` to +form a `data.Reader` which can provide data for training, validation and +testing respectively. Users can simply call `Reader.[train|eval|infer]` to get +the corresponding data stream. Many aspect of the `Reader`, such as storage +location, preprocessing pipeline, acceleration mode can be configured with yaml +files. + +### APIs + +The main APIs are as follows: + +1. Data parsing + + - `source/coco_loader.py`: COCO dataset parser. [source](../ppdet/data/source/coco_loader.py) + - `source/voc_loader.py`: Pascal VOC dataset parser. [source](../ppdet/data/source/voc_loader.py) + [Note] To use a non-default label list for VOC datasets, a `label_list.txt` + file is needed, one can use the provided label list + (`data/pascalvoc/ImageSets/Main/label_list.txt`) or generate a custom one (with `tools/generate_data_for_training.py`). Also, `use_default_label` option should + be set to `false` in the configuration file + - `source/loader.py`: Roidb dataset parser. [source](../ppdet/data/source/loader.py) + +2. Operator + `transform/operators.py`: Contains a variety of data augmentation methods, including: +- `DecodeImage`: Read images in RGB format. +- `RandomFlipImage`: Horizontal flip. +- `RandomDistort`: Distort brightness, contrast, saturation, and hue. +- `ResizeImage`: Resize image with interpolation. +- `RandomInterpImage`: Use a random interpolation method to resize the image. +- `CropImage`: Crop image with respect to different scale, aspect ratio, and overlap. +- `ExpandImage`: Pad image to a larger size, padding filled with mean image value. +- `NormalizeImage`: Normalize image pixel values. +- `NormalizeBox`: Normalize the bounding box. +- `Permute`: Arrange the channels of the image and optionally convert image to BGR format. +- `MixupImage`: Mixup two images with given fraction[1](#mix). + +[1] Please refer to [this paper](https://arxiv.org/pdf/1710.09412.pdf)。 + +`transform/arrange_sample.py`: Assemble the data samples needed by different models. +3. Transformer +`transform/post_map.py`: Transformations that operates on whole batches, mainly for: +- Padding whole batch to given stride values +- Resize images to Multi-scales +- Randomly adjust the image size of the batch data +`transform/transformer.py`: Data filtering batching. +`transform/parallel_map.py`: Accelerate data processing with multi-threads/multi-processes. +4. Reader +`reader.py`: Combine source and transforms, return batch data according to `max_iter`. +`data_feed.py`: Configure default parameters for `reader.py`. + + +### Usage + +#### Canned Datasets + +Preset for common datasets, e.g., `COCO` and `Pascal Voc` are included. In +most cases, user can simply use these canned dataset as is. Moreover, the +whole data pipeline is fully customizable through the yaml configuration files. + +#### Custom Datasets + +- Option 1: Convert the dataset to COCO format. +```sh + # a small utility (`tools/x2coco.py`) is provided to convert + # Labelme-annotated dataset or cityscape dataset to COCO format. + python ./ppdet/data/tools/x2coco.py --dataset_type labelme + --json_input_dir ./labelme_annos/ + --image_input_dir ./labelme_imgs/ + --output_dir ./cocome/ + --train_proportion 0.8 + --val_proportion 0.2 + --test_proportion 0.0 + # --dataset_type: The data format which is need to be converted. Currently supported are: 'labelme' and 'cityscape' + # --json_input_dir:The path of json files which are annotated by Labelme. + # --image_input_dir:The path of images. + # --output_dir:The path of coverted COCO dataset. + # --train_proportion:The train proportion of annatation data. + # --val_proportion:The validation proportion of annatation data. + # --test_proportion: The inference proportion of annatation data. +``` + +- Option 2: + +1. Add `source/XX_loader.py` and implement the `load` function, following the + example of `source/coco_loader.py` and `source/voc_loader.py`. +2. Modify the `load` function in `source/loader.py` to make use of the newly + added data loader. +3. Modify `/source/__init__.py` accordingly. +```python +if data_cf['type'] in ['VOCSource', 'COCOSource', 'RoiDbSource']: + source_type = 'RoiDbSource' +# Replace the above code with the following code: +if data_cf['type'] in ['VOCSource', 'COCOSource', 'RoiDbSource', 'XXSource']: + source_type = 'RoiDbSource' +``` +4. In the configure file, define the `type` of `dataset` as `XXSource`. + +#### How to add data pre-processing? + +- To add pre-processing operation for a single image, refer to the classes in + `transform/operators.py`, and implement the desired transformation with a new + class. +- To add pre-processing for a batch, one needs to modify the `build_post_map` + function in `transform/post_map.py`. diff --git a/docs/DATA_cn.md b/docs/DATA_cn.md new file mode 100644 index 0000000000000000000000000000000000000000..332e81b0d313e9e8072979667b614de6924cec45 --- /dev/null +++ b/docs/DATA_cn.md @@ -0,0 +1,212 @@ +# 数据模块 + +## 介绍 +本模块是一个Python模块,用于加载数据并将其转换成适用于检测模型的训练、验证、测试所需要的格式——由多个np.ndarray组成的tuple数组,例如用于Faster R-CNN模型的训练数据格式为:`[(im, im_info, im_id, gt_bbox, gt_class, is_crowd), (...)]`。 + +### 实现 +该模块内部可分为4个子功能:数据解析、图片预处理、数据转换和数据获取接口。 + +我们采用`data.Dataset`表示一份数据,比如`COCO`数据包含3份数据,分别用于训练、验证和测试。原始数据存储与文件中,通过`data.source`加载到内存,然后使用`data.transform`对数据进行处理转换,最终通过`data.Reader`的接口可以获得用于训练、验证和测试的batch数据。 + +子功能介绍: + +1. 数据解析 + +数据解析得到的是`data.Dataset`,实现逻辑位于`data.source`中。通过它可以实现解析不同格式的数据集,已支持的数据源包括: + +- COCO数据源 + +该数据集目前分为COCO2014和COCO2017,主要由json文件和image文件组成,其组织结构如下所示: + + ``` + dataset/coco/ + ├── annotations + │ ├── instances_train2014.json + │ ├── instances_train2017.json + │ ├── instances_val2014.json + │ ├── instances_val2017.json + │ | ... + ├── train2017 + │ ├── 000000000009.jpg + │ ├── 000000580008.jpg + │ | ... + ├── val2017 + │ ├── 000000000139.jpg + │ ├── 000000000285.jpg + │ | ... + | ... + ``` + + +- Pascal VOC数据源 + +该数据集目前分为VOC2007和VOC2012,主要由xml文件和image文件组成,其组织结构如下所示: + + ``` + dataset/voc/ + ├── train.txt + ├── val.txt + ├── test.txt + ├── label_list.txt (optional) + ├── VOCdevkit/VOC2007 + │ ├── Annotations + │ ├── 001789.xml + │ | ... + │ ├── JPEGImages + │ ├── 001789.xml + │ | ... + │ ├── ImageSets + │ | ... + ├── VOCdevkit/VOC2012 + │ ├── Annotations + │ ├── 003876.xml + │ | ... + │ ├── JPEGImages + │ ├── 003876.xml + │ | ... + │ ├── ImageSets + │ | ... + | ... + ``` + +**说明:** 如果你在yaml配置文件中设置`use_default_label=False`, 将从`label_list.txt` +中读取类别列表,反之则可以没有`label_list.txt`文件,检测库会使用Pascal VOC数据集的默 +认类别列表,默认类别列表定义在[voc\_loader.py](../ppdet/data/source/voc_loader.py) + +- Roidb数据源 + 该数据集主要由COCO数据集和Pascal VOC数据集转换而成的pickle文件,包含一个dict,而dict中只包含一个命名为‘records’的list(可能还有一个命名为‘cname2cid’的字典),其内容如下所示: +```python +(records, catname2clsid) +'records'是一个list并且它的结构如下: +{ + 'im_file': im_fname, # 图像文件名 + 'im_id': im_id, # 图像id + 'h': im_h, # 图像高度 + 'w': im_w, # 图像宽度 + 'is_crowd': is_crowd, # 是否重叠 + 'gt_class': gt_class, # 真实框类别 + 'gt_bbox': gt_bbox, # 真实框坐标 + 'gt_poly': gt_poly, # 多边形坐标 +} +'cname2id'是一个dict,保存了类别名到id的映射 + +``` +我们在`./tools/`中提供了一个生成roidb数据集的代码,可以通过下面命令实现该功能。 +``` +# --type: 原始数据集的类别(只能是xml或者json) +# --annotation: 一个包含所需标注文件名的文件的路径 +# --save-dir: 保存路径 +# --samples: sample的个数(默认是-1,代表使用所有sample) +python ./ppdet/data/tools/generate_data_for_training.py + --type=json \ + --annotation=./annotations/instances_val2017.json \ + --save-dir=./roidb \ + --samples=-1 +``` + 2. 图片预处理 + 图片预处理通过包括图片解码、缩放、裁剪等操作,我们采用`data.transform.operator`算子的方式来统一实现,这样能方便扩展。此外,多个算子还可以组合形成复杂的处理流程, 并被`data.transformer`中的转换器使用,比如多线程完成一个复杂的预处理流程。 + + 3. 数据转换器 + 数据转换器的功能是完成对某个`data.Dataset`进行转换处理,从而得到一个新的`data.Dataset`。我们采用装饰器模式实现各种不同的`data.transform.transformer`。比如用于多进程预处理的`dataset.transform.paralle_map`转换器。 + + 4. 数据获取接口 + 为方便训练时的数据获取,我们将多个`data.Dataset`组合在一起构成一个`data.Reader`为用户提供数据,用户只需要调用`Reader.[train|eval|infer]`即可获得对应的数据流。`Reader`支持yaml文件配置数据地址、预处理过程、加速方式等。 + +### APIs + +主要的APIs如下: + + +1. 数据解析 + + - `source/coco_loader.py`:用于解析COCO数据集。[详见代码](../ppdet/data/source/coco_loader.py) + - `source/voc_loader.py`:用于解析Pascal VOC数据集。[详见代码](../ppdet/data/source/voc_loader.py) + [注意]在使用VOC数据集时,若不使用默认的label列表,则需要先使用`tools/generate_data_for_training.py`生成`label_list.txt`(使用方式与数据解析中的roidb数据集获取过程一致),或提供`label_list.txt`放置于`data/pascalvoc/ImageSets/Main`中;同时在配置文件中设置参数`use_default_label`为`true`。 + - `source/loader.py`:用于解析Roidb数据集。[详见代码](../ppdet/data/source/loader.py) + +2. 算子 + `transform/operators.py`:包含多种数据增强方式,主要包括: + +``` python +RandomFlipImage:水平翻转。 +RandomDistort:随机扰动图片亮度、对比度、饱和度和色相。 +ResizeImage:根据特定的插值方式调整图像大小。 +RandomInterpImage:使用随机的插值方式调整图像大小。 +CropImage:根据缩放比例、长宽比例两个参数生成若干候选框,再依据这些候选框和标注框的面积交并比(IoU)挑选出符合要求的裁剪结果。 +ExpandImage:将原始图片放进一张使用像素均值填充(随后会在减均值操作中减掉)的扩张图中,再对此图进行裁剪、缩放和翻转。 +DecodeImage:以RGB格式读取图像。 +Permute:对图像的通道进行排列并转为BGR格式。 +NormalizeImage:对图像像素值进行归一化。 +NormalizeBox:对bounding box进行归一化。 +MixupImage:按比例叠加两张图像。 +``` +[注意]:Mixup的操作可参考[论文](https://arxiv.org/pdf/1710.09412.pdf)。 + +`transform/arrange_sample.py`:实现对输入网络数据的排序。 +3. 转换 +`transform/post_map.py`:用于完成批数据的预处理操作,其主要包括: + +``` python +随机调整批数据的图像大小 +多尺度调整图像大小 +padding操作 +``` +`transform/transformer.py`:用于过滤无用的数据,并返回批数据。 +`transform/parallel_map.py`:用于实现加速。 +4. 读取 +`reader.py`:用于组合source和transformer操作,根据`max_iter`返回batch数据。 +`data_feed.py`: 用于配置 `reader.py`中所需的默认参数. + + + + +### 使用 +#### 常规使用 +结合yaml文件中的配置信息,完成本模块的功能。yaml文件的使用可以参见配置文件部分。 + + - 读取用于训练的数据 + +``` python +ccfg = load_cfg('./config.yml') +coco = Reader(ccfg.DATA, ccfg.TRANSFORM, maxiter=-1) +``` +#### 如何使用自定义数据集? + +- 选择1:将数据集转换为COCO格式。 +``` + # 在./tools/中提供了x2coco.py用于将labelme标注的数据集或cityscape数据集转换为COCO数据集 + python ./ppdet/data/tools/x2coco.py --dataset_type labelme + --json_input_dir ./labelme_annos/ + --image_input_dir ./labelme_imgs/ + --output_dir ./cocome/ + --train_proportion 0.8 + --val_proportion 0.2 + --test_proportion 0.0 + # --dataset_type:需要转换的数据格式,目前支持:’labelme‘和’cityscape‘ + # --json_input_dir:使用labelme标注的json文件所在文件夹 + # --image_input_dir:图像文件所在文件夹 + # --output_dir:转换后的COCO格式数据集存放位置 + # --train_proportion:标注数据中用于train的比例 + # --val_proportion:标注数据中用于validation的比例 + # --test_proportion: 标注数据中用于infer的比例 +``` +- 选择2: + +1. 仿照`./source/coco_loader.py`和`./source/voc_loader.py`,添加`./source/XX_loader.py`并实现`load`函数。 +2. 在`./source/loader.py`的`load`函数中添加使用`./source/XX_loader.py`的入口。 +3. 修改`./source/__init__.py`: + + +```python +if data_cf['type'] in ['VOCSource', 'COCOSource', 'RoiDbSource']: + source_type = 'RoiDbSource' +# 将上述代码替换为如下代码: +if data_cf['type'] in ['VOCSource', 'COCOSource', 'RoiDbSource', 'XXSource']: + source_type = 'RoiDbSource' +``` + +4. 在配置文件中修改`dataset`下的`type`为`XXSource`。 + +#### 如何增加数据预处理? +- 若增加单张图像的增强预处理,可在`transform/operators.py`中参考每个类的代码,新建一个类来实现新的数据增强;同时在配置文件中增加该预处理。 +- 若增加单个batch的图像预处理,可在`transform/post_map.py`中参考`build_post_map`中每个函数的代码,新建一个内部函数来实现新的批数据预处理;同时在配置文件中增加该预处理。 diff --git a/docs/EXPORT_MODEL.md b/docs/EXPORT_MODEL.md new file mode 100644 index 0000000000000000000000000000000000000000..614d87e29c5e5d5685f64c9ee78cf7d1d5192d09 --- /dev/null +++ b/docs/EXPORT_MODEL.md @@ -0,0 +1,48 @@ +# 模型导出 + +训练得到一个满足要求的模型后,如果想要将该模型接入到C++预测库或者Serving服务,需要通过`tools/export_model.py`导出该模型。 + +## 启动参数说明 + +| FLAG | 用途 | 默认值 | 备注 | +|:--------------:|:--------------:|:------------:|:-----------------------------------------:| +| -c | 指定配置文件 | None | | +| --output_dir | 模型保存路径 | `./output` | 模型默认保存在`output/配置文件名/`路径下 | + +## 使用示例 + +使用[训练/评估/推断](GETTING_STARTED_cn.md)中训练得到的模型进行试用,脚本如下 + +```bash +# 导出FasterRCNN模型, 模型中data层默认的shape为3x800x1333 +python tools/export_model.py -c configs/faster_rcnn_r50_1x.yml \ + --output_dir=./inference_model \ + -o weights=output/faster_rcnn_r50_1x/model_final \ + +``` + +预测模型会导出到`inference_model/faster_rcnn_r50_1x`目录下,模型名和参数名分别为`__model__`和`__params__`。 + +## 设置导出模型的输入大小 + +使用Fluid-TensorRT进行预测时,由于<=TensorRT 5.1的版本仅支持定长输入,保存模型的`data`层的图片大小需要和实际输入图片大小一致。而Fluid C++预测引擎没有此限制。可通过设置TestFeed的`image_shape`可以修改保存模型中的输入图片大小。示例如下: + +```bash +# 导出FasterRCNN模型,输入是3x640x640 +python tools/export_model.py -c configs/faster_rcnn_r50_1x.yml \ + --output_dir=./inference_model \ + -o weights=https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_1x.tar \ + FasterRCNNTestFeed.image_shape=[3,640,640] + +# 导出YOLOv3模型,输入是3x320x320 +python tools/export_model.py -c configs/yolov3_darknet.yml \ + --output_dir=./inference_model \ + -o weights=https://paddlemodels.bj.bcebos.com/object_detection/yolov3_darknet.tar \ + YoloTestFeed.image_shape=[3,320,320] + +# 导出SSD模型,输入是3x300x300 +python tools/export_model.py -c configs/ssd/ssd_mobilenet_v1_voc.yml \ + --output_dir=./inference_model \ + -o weights= https://paddlemodels.bj.bcebos.com/object_detection/ssd_mobilenet_v1_voc.tar \ + SSDTestFeed.image_shape=[3,300,300] +``` diff --git a/docs/GETTING_STARTED.md b/docs/GETTING_STARTED.md new file mode 100644 index 0000000000000000000000000000000000000000..199b343ed2a80a1bf80fcf3d3206fbdf8413551f --- /dev/null +++ b/docs/GETTING_STARTED.md @@ -0,0 +1,187 @@ +English | [简体中文](GETTING_STARTED_cn.md) + +# Getting Started + +For setting up the running environment, please refer to [installation +instructions](INSTALL.md). + + +## Training/Evaluation/Inference + +PaddleDetection provides scripots for training, evalution and inference with various features according to different configure. + +```bash +# set PYTHONPATH +export PYTHONPATH=$PYTHONPATH:. +# training in single-GPU and multi-GPU. specify different GPU numbers by CUDA_VISIBLE_DEVICES +export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 +python tools/train.py -c configs/faster_rcnn_r50_1x.yml +# GPU evalution +export CUDA_VISIBLE_DEVICES=0 +python tools/eval.py -c configs/faster_rcnn_r50_1x.yml +# Inference +python tools/infer.py -c configs/faster_rcnn_r50_1x.yml --infer_img=demo/000000570688.jpg +``` + +### Optional argument list + +list below can be viewed by `--help` + +| FLAG | script supported | description | default | remark | +| :----------------------: | :------------: | :---------------: | :--------------: | :-----------------: | +| -c | ALL | Select config file | None | **The whole description of configure can refer to [config_example](config_example)** | +| -o | ALL | Set parameters in configure file | None | `-o` has higher priority to file configured by `-c`. Such as `-o use_gpu=False max_iter=10000` | +| -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` | +| --output_dir | infer | Directory for storing the output visualization files | `./output` | `--output_dir output` | +| --draw_threshold | infer | Threshold to reserve the result for visualization | 0.5 | `--draw_threshold 0.7` | +| --infer_dir | infer | Directory for images to perform inference on | None | | +| --infer_img | infer | Image path | None | higher priority over --infer_dir | +| --use_tb | train/infer | Whether to record the data with [tb-paddle](https://github.com/linshuliang/tb-paddle), so as to display in Tensorboard | False | | +| --tb\_log_dir | train/infer | tb-paddle logging directory for image | train:`tb_log_dir/scalar` infer: `tb_log_dir/image` | | + + +## Examples + +### Training + +- Perform evaluation in training + + ```bash + export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 + python -u tools/train.py -c configs/faster_rcnn_r50_1x.yml --eval + ``` + + Perform training and evalution alternatively and evaluate at each snapshot_iter. Meanwhile, the best model with highest MAP is saved at each `snapshot_iter` which has the same path as `model_final`. + + If evaluation dataset is large, we suggest decreasing evaluation times or evaluating after training. + +- Fine-tune other task + + When using pre-trained model to fine-tune other task, two methods can be used: + + 1. The excluded pre-trained parameters can be set by `finetune_exclude_pretrained_params` in YAML config + 2. Set -o finetune\_exclude\_pretrained_params in the arguments. + + ```bash + export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 + python -u tools/train.py -c configs/faster_rcnn_r50_1x.yml \ + -o pretrain_weights=output/faster_rcnn_r50_1x/model_final/ \ + finetune_exclude_pretrained_params = ['cls_score','bbox_pred'] + ``` + +##### NOTES + +- `CUDA_VISIBLE_DEVICES` can specify different gpu numbers. Such as: `export CUDA_VISIBLE_DEVICES=0,1,2,3`. GPU calculation rules can refer [FAQ](#faq) +- Dataset will be downloaded automatically and cached in `~/.cache/paddle/dataset` if not be found locally. +- Pretrained model is downloaded automatically and cached in `~/.cache/paddle/weights`. +- Checkpoints are saved in `output` by default, and can be revised from save_dir in configure files. +- RCNN models training on CPU is not supported on PaddlePaddle<=1.5.1 and will be fixed on later version. + + +### Mixed Precision Training + +Mixed precision training can be enabled with `--fp16` flag. Currently Faster-FPN, Mask-FPN and Yolov3 have been verified to be working with little to no loss of precision (less than 0.2 mAP) + +To speed up mixed precision training, it is recommended to train in multi-process mode, for example + +```bash +python -m paddle.distributed.launch --selected_gpus 0,1,2,3,4,5,6,7 tools/train.py --fp16 -c configs/faster_rcnn_r50_fpn_1x.yml +``` + +If loss becomes `NaN` during training, try tweak the `--loss_scale` value. Please refer to the Nvidia [documentation](https://docs.nvidia.com/deeplearning/sdk/mixed-precision-training/index.html#mptrain) on mixed precision training for details. + +Also, please note mixed precision training currently requires changing `norm_type` from `affine_channel` to `bn`. + + + +### Evaluation + +- Evaluate by specified weights path and dataset path + + ```bash + export CUDA_VISIBLE_DEVICES=0 + python -u tools/eval.py -c configs/faster_rcnn_r50_1x.yml \ + -o weights=https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_1x.tar \ + -d dataset/coco + ``` + + The path of model to be evaluted can be both local path and link in [MODEL_ZOO](MODEL_ZOO_cn.md). + +- Evaluate with json + + ```bash + export CUDA_VISIBLE_DEVICES=0 + python tools/eval.py -c configs/faster_rcnn_r50_1x.yml \ + --json_eval \ + -f evaluation/ + ``` + + The json file must be named bbox.json or mask.json, placed in the `evaluation/` directory. + +#### NOTES + +- Multi-GPU evaluation for R-CNN and SSD models is not supported at the +moment, but it is a planned feature + + +### Inference + +- Output specified directory && Set up threshold + + ```bash + export CUDA_VISIBLE_DEVICES=0 + python tools/infer.py -c configs/faster_rcnn_r50_1x.yml \ + --infer_img=demo/000000570688.jpg \ + --output_dir=infer_output/ \ + --draw_threshold=0.5 \ + -o weights=output/faster_rcnn_r50_1x/model_final \ + --use_tb=Ture + ``` + + `--draw_threshold` is an optional argument. Default is 0.5. + Different thresholds will produce different results depending on the calculation of [NMS](https://ieeexplore.ieee.org/document/1699659). + + +- Export model + + ```bash + python tools/export_model.py -c configs/faster_rcnn_r50_1x.yml \ + --output_dir=inference_model \ + -o weights=output/faster_rcnn_r50_1x/model_final \ + FasterRCNNTestFeed.image_shape=[3,800,1333] + ``` + + Save inference model `tools/export_model.py`, which can be loaded by PaddlePaddle predict library. + +## FAQ + +**Q:** Why do I get `NaN` loss values during single GPU training?
+**A:** The default learning rate is tuned to multi-GPU training (8x GPUs), it must +be adapted for single GPU training accordingly (e.g., divide by 8). +The calculation rules are as follows,they are equivalent:
+ + +| GPU number | Learning rate | Max_iters | Milestones | +| :---------: | :------------: | :-------: | :--------------: | +| 2 | 0.0025 | 720000 | [480000, 640000] | +| 4 | 0.005 | 360000 | [240000, 320000] | +| 8 | 0.01 | 180000 | [120000, 160000] | + + +**Q:** How to reduce GPU memory usage?
+**A:** Setting environment variable FLAGS_conv_workspace_size_limit to a smaller +number can reduce GPU memory footprint without affecting training speed. +Take Mask-RCNN (R50) as example, by setting `export FLAGS_conv_workspace_size_limit=512`, +batch size could reach 4 per GPU (Tesla V100 16GB). + + +**Q:** How to change data preprocessing?
+**A:** Set `sample_transform` in configuration. Note that **the whole transforms** need to be added in configuration. +For example, `DecodeImage`, `NormalizeImage` and `Permute` in RCNN models. For detail description, please refer +to [config_example](config_example). diff --git a/docs/GETTING_STARTED_cn.md b/docs/GETTING_STARTED_cn.md new file mode 100644 index 0000000000000000000000000000000000000000..b5dd6041033e539e18d59dc8669a3658ba395da2 --- /dev/null +++ b/docs/GETTING_STARTED_cn.md @@ -0,0 +1,173 @@ +# 开始 + +关于配置运行环境,请参考[安装指南](INSTALL_cn.md) + + +## 训练/评估/推断 + +PaddleDetection提供了训练/训练/评估三个功能的使用脚本,支持通过不同可选参数实现特定功能 + +```bash +# 设置PYTHONPATH路径 +export PYTHONPATH=$PYTHONPATH:. +# GPU训练 支持单卡,多卡训练,通过CUDA_VISIBLE_DEVICES指定卡号 +export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 +python tools/train.py -c configs/faster_rcnn_r50_1x.yml +# GPU评估 +export CUDA_VISIBLE_DEVICES=0 +python tools/eval.py -c configs/faster_rcnn_r50_1x.yml +# 推断 +python tools/infer.py -c configs/faster_rcnn_r50_1x.yml --infer_img=demo/000000570688.jpg +``` + +### 可选参数列表 + +以下列表可以通过`--help`查看 + +| FLAG | 支持脚本 | 用途 | 默认值 | 备注 | +| :----------------------: | :------------: | :---------------: | :--------------: | :-----------------: | +| -c | ALL | 指定配置文件 | None | **完整配置说明请参考[配置案例](config_example)** | +| -o | ALL | 设置配置文件里的参数内容 | None | 使用-o配置相较于-c选择的配置文件具有更高的优先级。例如:`-o use_gpu=False max_iter=10000` | +| -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`中设置 | +| --output_dir | infer | 输出推断后可视化文件 | `./output` | `--output_dir output` | +| --draw_threshold | infer | 可视化时分数阈值 | 0.5 | `--draw_threshold 0.7` | +| --infer_dir | infer | 用于推断的图片文件夹路径 | None | | +| --infer_img | infer | 用于推断的图片路径 | None | 相较于`--infer_dir`具有更高优先级 | +| --use_tb | train/infer | 是否使用[tb-paddle](https://github.com/linshuliang/tb-paddle)记录数据,进而在TensorBoard中显示 | False | | +| --tb\_log_dir | train/infer | 指定 tb-paddle 记录数据的存储路径 | train:`tb_log_dir/scalar` infer: `tb_log_dir/image` | | + + +## 使用示例 + +### 模型训练 + +- 边训练边测试 + + ```bash + export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 + python -u tools/train.py -c configs/faster_rcnn_r50_1x.yml --eval -d dataset/coco + ``` + + 在训练中交替执行评估, 评估在每个snapshot\_iter时开始。每次评估后还会评出最佳mAP模型保存到`best_model`文件夹下。 + + 如果验证集很大,测试将会比较耗时,建议减少评估次数,或训练完再进行评估。 + + +- Fine-tune其他任务 + + 使用预训练模型fine-tune其他任务时,可采用如下两种方式: + + 1. 在YAML配置文件中设置`finetune_exclude_pretrained_params` + 2. 在命令行中添加-o finetune\_exclude\_pretrained_params对预训练模型进行选择性加载。 + + ```bash + export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 + python -u tools/train.py -c configs/faster_rcnn_r50_1x.yml \ + -o pretrain_weights=output/faster_rcnn_r50_1x/model_final/ \ + finetune_exclude_pretrained_params=['cls_score','bbox_pred'] + ``` + + 详细说明请参考[Transfer Learning](TRANSFER_LEARNING_cn.md) + +#### 提示 + +- `CUDA_VISIBLE_DEVICES` 参数可以指定不同的GPU。例如: `export CUDA_VISIBLE_DEVICES=0,1,2,3`. GPU计算规则可以参考 [FAQ](#faq) +- 若本地未找到数据集,将自动下载数据集并保存在`~/.cache/paddle/dataset`中。 +- 预训练模型自动下载并保存在`〜/.cache/paddle/weights`中。 +- 模型checkpoints默认保存在`output`中,可通过修改配置文件中save_dir进行配置。 +- RCNN系列模型CPU训练在PaddlePaddle 1.5.1及以下版本暂不支持。 + +### 混合精度训练 + +通过设置 `--fp16` 命令行选项可以启用混合精度训练。目前混合精度训练已经在Faster-FPN, Mask-FPN 及 Yolov3 上进行验证,几乎没有精度损失(小于0.2 mAP)。 + +建议使用多进程方式来进一步加速混合精度训练。示例如下。 + +```bash +python -m paddle.distributed.launch --selected_gpus 0,1,2,3,4,5,6,7 tools/train.py --fp16 -c configs/faster_rcnn_r50_fpn_1x.yml +``` + +如果训练过程中loss出现`NaN`,请尝试调节`--loss_scale`选项数值,细节请参看混合精度训练相关的[Nvidia文档](https://docs.nvidia.com/deeplearning/sdk/mixed-precision-training/index.html#mptrain)。 + +另外,请注意将配置文件中的 `norm_type` 由 `affine_channel` 改为 `bn`。 + + +### 模型评估 + +- 指定权重和数据集路径 + + ```bash + export CUDA_VISIBLE_DEVICES=0 + python -u tools/eval.py -c configs/faster_rcnn_r50_1x.yml \ + -o weights=https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_1x.tar \ + -d dataset/coco + ``` + + 评估模型可以为本地路径,例如`output/faster_rcnn_r50_1x/model_final/`, 也可以为[MODEL_ZOO](MODEL_ZOO_cn.md)中给出的模型链接。 + +- 通过json文件评估 + + ```bash + export CUDA_VISIBLE_DEVICES=0 + python -u tools/eval.py -c configs/faster_rcnn_r50_1x.yml \ + --json_eval \ + --output_eval evaluation/ + ``` + + json文件必须命名为bbox.json或者mask.json,放在`evaluation/`目录下。 + +#### 提示 + +- R-CNN和SSD模型目前暂不支持多GPU评估,将在后续版本支持 + + +### 模型推断 + +- 设置输出路径 && 设置推断阈值 + + ```bash + export CUDA_VISIBLE_DEVICES=0 + python -u tools/infer.py -c configs/faster_rcnn_r50_1x.yml \ + --infer_img=demo/000000570688.jpg \ + --output_dir=infer_output/ \ + --draw_threshold=0.5 \ + -o weights=output/faster_rcnn_r50_1x/model_final \ + ``` + + + `--draw_threshold` 是个可选参数. 根据 [NMS](https://ieeexplore.ieee.org/document/1699659) 的计算, + 不同阈值会产生不同的结果。如果用户需要对自定义路径的模型进行推断,可以设置`-o weights`指定模型路径。 + +## FAQ + +**Q:** 为什么我使用单GPU训练loss会出`NaN`?
+**A:** 默认学习率是适配多GPU训练(8x GPU),若使用单GPU训练,须对应调整学习率(例如,除以8)。 +计算规则表如下所示,它们是等价的:
+ + +| GPU数 | 学习率 | 最大轮数 | 变化节点 | +| :---------: | :------------: | :-------: | :--------------: | +| 2 | 0.0025 | 720000 | [480000, 640000] | +| 4 | 0.005 | 360000 | [240000, 320000] | +| 8 | 0.01 | 180000 | [120000, 160000] | + + +**Q:** 如何减少GPU显存使用率?
+**A:** 可通过设置环境变量`FLAGS_conv_workspace_size_limit`为较小的值来减少显存消耗,并且不 +会影响训练速度。以Mask-RCNN(R50)为例,设置`export FLAGS_conv_workspace_size_limit = 512`, +batch size可以达到每GPU 4 (Tesla V100 16GB)。 + + +**Q:** 如何修改数据预处理?
+**A:** 可在配置文件中设置 `sample_transform`。注意需要在配置文件中加入**完整预处理** +例如RCNN模型中`DecodeImage`, `NormalizeImage` and `Permute`。更多详细描述请参考[配置案例](config_example)。 + + +**Q:** affine_channel和batch norm是什么关系? +**A:** 在RCNN系列模型加载预训练模型初始化,有时候会固定住batch norm的参数, 使用预训练模型中的全局均值和方式,并且batch norm的scale和bias参数不更新,已发布的大多ResNet系列的RCNN模型采用这种方式。这种情况下可以在config中设置norm_type为bn或affine_channel, freeze_norm为true (默认为true),两种方式等价。affne_channel的计算方式为`scale * x + bias`。只不过设置affine_channel时,内部对batch norm的参数自动做了融合。如果训练使用的affine_channel,用保存的模型做初始化,训练其他任务时,即可使用affine_channel, 也可使用batch norm, 参数均可正确加载。 diff --git a/docs/INSTALL.md b/docs/INSTALL.md new file mode 100644 index 0000000000000000000000000000000000000000..6a9baf0a4aa7078d27ee654764e9a99cc49071be --- /dev/null +++ b/docs/INSTALL.md @@ -0,0 +1,205 @@ +English | [简体中文](INSTALL_cn.md) + +# Installation + +--- +## Table of Contents + +- [Introduction](#introduction) +- [PaddlePaddle](#paddlepaddle) +- [Other Dependencies](#other-dependencies) +- [PaddleDetection](#paddle-detection) +- [Datasets](#datasets) + + +## Introduction + +This document covers how to install PaddleDetection, its dependencies +(including PaddlePaddle), together with COCO and Pascal VOC dataset. + +For general information about PaddleDetection, please see [README.md](../README.md). + + +## PaddlePaddle + +Running PaddleDetection requires PaddlePaddle Fluid v.1.5 and later. please follow the instructions in [installation document](http://www.paddlepaddle.org.cn/). + +Please make sure your PaddlePaddle installation was successful and the version +of your PaddlePaddle is not lower than required. Verify with the following commands. + +``` +# To check PaddlePaddle installation in your Python interpreter +>>> import paddle.fluid as fluid +>>> fluid.install_check.run_check() + +# To check PaddlePaddle version +python -c "import paddle; print(paddle.__version__)" +``` + +### Requirements: + +- Python2 or Python3 (Only support Python3 for windows) +- CUDA >= 8.0 +- cuDNN >= 5.0 +- nccl >= 2.1.2 + + +## Other Dependencies + +[COCO-API](https://github.com/cocodataset/cocoapi): + +COCO-API is needed for running. Installation is as follows: + + git clone https://github.com/cocodataset/cocoapi.git + cd cocoapi/PythonAPI + # if cython is not installed + pip install Cython + # Install into global site-packages + make install + # Alternatively, if you do not have permissions or prefer + # not to install the COCO API into global site-packages + python setup.py install --user + +**Installation of COCO-API in windows:** + + # if cython is not installed + pip install Cython + # Because the origin version of cocoapi does not support windows, another version is used which only supports Python3 + pip install git+https://github.com/philferriere/cocoapi.git#subdirectory=PythonAPI + +## PaddleDetection + +**Clone Paddle models repository:** + +You can clone Paddle models and change working directory to PaddleDetection +with the following commands: + +``` +cd +git clone https://github.com/PaddlePaddle/models +cd models/PaddleCV/PaddleDetection +``` + +**Install Python dependencies:** + +Required python packages are specified in [requirements.txt](../requirements.txt), and can be installed with: + +``` +pip install -r requirements.txt +``` + +**Make sure the tests pass:** + +``` +export PYTHONPATH=`pwd`:$PYTHONPATH +python ppdet/modeling/tests/test_architectures.py +``` + + +## Datasets + +PaddleDetection includes support for [COCO](http://cocodataset.org) and [Pascal VOC](http://host.robots.ox.ac.uk/pascal/VOC/) by default, please follow these instructions to set up the dataset. + +**Create symlinks for local datasets:** + +Default dataset path in config files is `dataset/coco` and `dataset/voc`, if the +datasets are already available on disk, you can simply create symlinks to +their directories: + +``` +ln -sf /dataset/coco +ln -sf /dataset/voc +``` + +For Pascal VOC dataset, you should create file list by: + +``` +export PYTHONPATH=$PYTHONPATH:. +python dataset/voc/create_list.py +``` + +**Download datasets manually:** + +On the other hand, to download the datasets, run the following commands: + +- COCO + +``` +export PYTHONPATH=$PYTHONPATH:. +python dataset/coco/download_coco.py +``` + +`COCO` dataset with directory structures like this: + + ``` + dataset/coco/ + ├── annotations + │ ├── instances_train2014.json + │ ├── instances_train2017.json + │ ├── instances_val2014.json + │ ├── instances_val2017.json + │ | ... + ├── train2017 + │ ├── 000000000009.jpg + │ ├── 000000580008.jpg + │ | ... + ├── val2017 + │ ├── 000000000139.jpg + │ ├── 000000000285.jpg + │ | ... + | ... + ``` + +- Pascal VOC + +``` +export PYTHONPATH=$PYTHONPATH:. +python dataset/voc/download_voc.py +python dataset/voc/create_list.py +``` + +`Pascal VOC` dataset with directory structure like this: + + ``` + dataset/voc/ + ├── train.txt + ├── val.txt + ├── test.txt + ├── label_list.txt (optional) + ├── VOCdevkit/VOC2007 + │ ├── Annotations + │ ├── 001789.xml + │ | ... + │ ├── JPEGImages + │ ├── 001789.xml + │ | ... + │ ├── ImageSets + │ | ... + ├── VOCdevkit/VOC2012 + │ ├── Annotations + │ ├── 003876.xml + │ | ... + │ ├── JPEGImages + │ ├── 003876.xml + │ | ... + │ ├── ImageSets + │ | ... + | ... + ``` + +**NOTE:** If you set `use_default_label=False` in yaml configs, the `label_list.txt` +of Pascal VOC dataset will be read, otherwise, `label_list.txt` is unnecessary and +the default Pascal VOC label list which defined in +[voc\_loader.py](../ppdet/data/source/voc_loader.py) will be used. + +**Download datasets automatically:** + +If a training session is started but the dataset is not setup properly (e.g, +not found in `dataset/coco` or `dataset/voc`), PaddleDetection can automatically +download them from [COCO-2017](http://images.cocodataset.org) and +[VOC2012](http://host.robots.ox.ac.uk/pascal/VOC), the decompressed datasets +will be cached in `~/.cache/paddle/dataset/` and can be discovered automatically +subsequently. + + +**NOTE:** For further informations on the datasets, please see [DATA.md](DATA.md) diff --git a/docs/INSTALL_cn.md b/docs/INSTALL_cn.md new file mode 100644 index 0000000000000000000000000000000000000000..f8eee189be251ecbf793689d01007f0275cae99c --- /dev/null +++ b/docs/INSTALL_cn.md @@ -0,0 +1,199 @@ +# 安装文档 + +--- +## 目录 + +- [简介](#introduction) +- [PaddlePaddle](#paddlepaddle) +- [其他依赖安装](#other-dependencies) +- [PaddleDetection](#paddle-detection) +- [数据集](#datasets) + + +## 简介 + +这份文档介绍了如何安装PaddleDetection及其依赖项(包括PaddlePaddle),以及COCO和Pascal VOC数据集。 + +PaddleDetection的相关信息,请参考[README.md](../README.md). + + +## PaddlePaddle + + +运行PaddleDetection需要PaddlePaddle Fluid v.1.5及更高版本。请按照[安装文档](http://www.paddlepaddle.org.cn/)中的说明进行操作。 + +请确保您的PaddlePaddle安装成功并且版本不低于需求版本。使用以下命令进行验证。 + +``` +# 在您的Python解释器中确认PaddlePaddle安装成功 +>>> import paddle.fluid as fluid +>>> fluid.install_check.run_check() + +# 确认PaddlePaddle版本 +python -c "import paddle; print(paddle.__version__)" +``` + +### 环境需求: + +- Python2 or Python3 (windows系统仅支持Python3) +- CUDA >= 8.0 +- cuDNN >= 5.0 +- nccl >= 2.1.2 + + +## 其他依赖安装 + +[COCO-API](https://github.com/cocodataset/cocoapi): + +运行需要COCO-API,安装方式如下: + + git clone https://github.com/cocodataset/cocoapi.git + cd cocoapi/PythonAPI + # 若Cython未安装,请安装Cython + pip install Cython + # 安装至全局site-packages + make install + # 若您没有权限或更倾向不安装至全局site-packages + python setup.py install --user + +**windows用户安装COCO-API方式:** + + # 若Cython未安装,请安装Cython + pip install Cython + # 由于原版cocoapi不支持windows,采用第三方实现版本,该版本仅支持Python3 + pip install git+https://github.com/philferriere/cocoapi.git#subdirectory=PythonAPI + +## PaddleDetection + +**克隆Paddle models模型库:** + +您可以通过以下命令克隆Paddle models模型库并切换工作目录至PaddleDetection: + +``` +cd +git clone https://github.com/PaddlePaddle/models +cd models/PaddleCV/PaddleDetection +``` + +**安装Python依赖库:** + +Python依赖库在[requirements.txt](../requirements.txt)中给出,可通过如下命令安装: + +``` +pip install -r requirements.txt +``` + +**确认测试通过:** + +``` +export PYTHONPATH=`pwd`:$PYTHONPATH +python ppdet/modeling/tests/test_architectures.py +``` + + +## 数据集 + + +PaddleDetection默认支持[COCO](http://cocodataset.org)和[Pascal VOC](http://host.robots.ox.ac.uk/pascal/VOC/), +请按照如下步骤设置数据集。 + +**为本地数据集创建软链接:** + + +配置文件中默认的数据集路径是`dataset/coco`和`dataset/voc`,如果您本地磁盘上已有数据集, +只需创建软链接至数据集目录: + +``` +ln -sf /dataset/coco +ln -sf /dataset/voc +``` + +对于Pascal VOC数据集,需通过如下命令创建文件列表: + +``` +export PYTHONPATH=$PYTHONPATH:. +python dataset/voc/create_list.py +``` + +**手动下载数据集:** + +若您本地没有数据集,可通过如下命令下载: + +- COCO + +``` +export PYTHONPATH=$PYTHONPATH:. +python dataset/coco/download_coco.py +``` + +`COCO` 数据集目录结构如下: + + ``` + dataset/coco/ + ├── annotations + │ ├── instances_train2014.json + │ ├── instances_train2017.json + │ ├── instances_val2014.json + │ ├── instances_val2017.json + │ | ... + ├── train2017 + │ ├── 000000000009.jpg + │ ├── 000000580008.jpg + │ | ... + ├── val2017 + │ ├── 000000000139.jpg + │ ├── 000000000285.jpg + │ | ... + | ... + ``` + +- Pascal VOC + +``` +export PYTHONPATH=$PYTHONPATH:. +python dataset/voc/download_voc.py +python dataset/voc/create_list.py +``` + +`Pascal VOC` 数据集目录结构如下: + + ``` + dataset/voc/ + ├── train.txt + ├── val.txt + ├── test.txt + ├── label_list.txt (optional) + ├── VOCdevkit/VOC2007 + │ ├── Annotations + │ ├── 001789.xml + │ | ... + │ ├── JPEGImages + │ ├── 001789.xml + │ | ... + │ ├── ImageSets + │ | ... + ├── VOCdevkit/VOC2012 + │ ├── Annotations + │ ├── 003876.xml + │ | ... + │ ├── JPEGImages + │ ├── 003876.xml + │ | ... + │ ├── ImageSets + │ | ... + | ... + ``` + +**说明:** 如果你在yaml配置文件中设置`use_default_label=False`, 将从`label_list.txt` +中读取类别列表,反之则可以没有`label_list.txt`文件,检测库会使用Pascal VOC数据集的默 +认类别列表,默认类别列表定义在[voc\_loader.py](../ppdet/data/source/voc_loader.py) + +**自动下载数据集:** + +若您在数据集未成功设置(例如,在`dataset/coco`或`dataset/voc`中找不到)的情况下开始运行, +PaddleDetection将自动从[COCO-2017](http://images.cocodataset.org)或 +[VOC2012](http://host.robots.ox.ac.uk/pascal/VOC)下载,解压后的数据集将被保存在 +`〜/.cache/paddle/dataset/`目录下,下次运行时,也可自动从该目录发现数据集。 + + +**说明:** 更多有关数据集的介绍,请参考[DATA.md](DATA_cn.md) diff --git a/docs/MODEL_ZOO.md b/docs/MODEL_ZOO.md new file mode 100644 index 0000000000000000000000000000000000000000..d6042ada1293ea77a1670871bbff1d6f94f8a163 --- /dev/null +++ b/docs/MODEL_ZOO.md @@ -0,0 +1,165 @@ +English | [简体中文](MODEL_ZOO_cn.md) + +# Model Zoo and Benchmark +## Environment + +- Python 2.7.1 +- PaddlePaddle >=1.5 +- CUDA 9.0 +- cuDNN >=7.4 +- NCCL 2.1.2 + +## Common settings + +- All models below were trained on `coco_2017_train`, and tested on `coco_2017_val`. +- Batch Normalization layers in backbones are replaced by Affine Channel layers. +- Unless otherwise noted, all ResNet backbones adopt the [ResNet-B](https://arxiv.org/pdf/1812.01187) variant.. +- For RCNN and RetinaNet models, only horizontal flipping data augmentation was used in the training phase and no augmentations were used in the testing phase. +- **Inf time (fps)**: the inference time is measured with fps (image/s) on a single GPU (Tesla V100) with cuDNN 7.5 by running 'tools/eval.py' on all validation set, which including data loadding, network forward and post processing. The batch size is 1. + + +## Training Schedules + +- We adopt exactly the same training schedules as [Detectron](https://github.com/facebookresearch/Detectron/blob/master/MODEL_ZOO.md#training-schedules). +- 1x indicates the schedule starts at a LR of 0.02 and is decreased by a factor of 10 after 60k and 80k iterations and eventually terminates at 90k iterations for minibatch size 16. For batch size 8, LR is decreased to 0.01, total training iterations are doubled, and the decay milestones are scaled by 2. +- 2x schedule is twice as long as 1x, with the LR milestones scaled accordingly. + +## ImageNet Pretrained Models + +The backbone models pretrained on ImageNet are available. All backbone models are pretrained on standard ImageNet-1k dataset and can be downloaded [here](https://github.com/PaddlePaddle/models/tree/develop/PaddleCV/image_classification#supported-models-and-performances). + +- **Notes:** The ResNet50 model was trained with cosine LR decay schedule and can be downloaded [here](https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_cos_pretrained.tar). + +## Baselines + +### Faster & Mask R-CNN + +| Backbone | Type | Image/gpu | Lr schd | Inf time (fps) | Box AP | Mask AP | Download | +| :---------------------- | :------------- | :-------: | :-----: | :------------: | :----: | :-----: | :----------------------------------------------------------: | +| ResNet50 | Faster | 1 | 1x | 12.747 | 35.2 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_1x.tar) | +| ResNet50 | Faster | 1 | 2x | 12.686 | 37.1 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_2x.tar) | +| ResNet50 | Mask | 1 | 1x | 11.615 | 36.5 | 32.2 | [model](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r50_1x.tar) | +| ResNet50 | Mask | 1 | 2x | 11.494 | 38.2 | 33.4 | [model](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r50_2x.tar) | +| ResNet50-vd | Faster | 1 | 1x | 12.575 | 36.4 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_vd_1x.tar) | +| ResNet50-FPN | Faster | 2 | 1x | 22.273 | 37.2 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_fpn_1x.tar) | +| ResNet50-FPN | Faster | 2 | 2x | 22.297 | 37.7 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_fpn_2x.tar) | +| ResNet50-FPN | Mask | 1 | 1x | 15.184 | 37.9 | 34.2 | [model](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r50_fpn_1x.tar) | +| ResNet50-FPN | Mask | 1 | 2x | 15.881 | 38.7 | 34.7 | [model](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r50_fpn_2x.tar) | +| ResNet50-FPN | Cascade Faster | 2 | 1x | 17.507 | 40.9 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/cascade_rcnn_r50_fpn_1x.tar) | +| ResNet50-FPN | Cascade Mask | 1 | 1x | - | 41.3 | 35.5 | [model](https://paddlemodels.bj.bcebos.com/object_detection/cascade_mask_rcnn_r50_fpn_1x.tar) | +| ResNet50-vd-FPN | Faster | 2 | 2x | 21.847 | 38.9 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_vd_fpn_2x.tar) | +| ResNet50-vd-FPN | Mask | 1 | 2x | 15.825 | 39.8 | 35.4 | [model](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r50_vd_fpn_2x.tar) | +| ResNet101 | Faster | 1 | 1x | 9.316 | 38.3 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r101_1x.tar) | +| ResNet101-FPN | Faster | 1 | 1x | 17.297 | 38.7 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r101_fpn_1x.tar) | +| ResNet101-FPN | Faster | 1 | 2x | 17.246 | 39.1 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r101_fpn_2x.tar) | +| ResNet101-FPN | Mask | 1 | 1x | 12.983 | 39.5 | 35.2 | [model](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r101_fpn_1x.tar) | +| ResNet101-vd-FPN | Faster | 1 | 1x | 17.011 | 40.5 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r101_vd_fpn_1x.tar) | +| ResNet101-vd-FPN | Faster | 1 | 2x | 16.934 | 40.8 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r101_vd_fpn_2x.tar) | +| ResNet101-vd-FPN | Mask | 1 | 1x | 13.105 | 41.4 | 36.8 | [model](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r101_vd_fpn_1x.tar) | +| ResNeXt101-vd-64x4d-FPN | Faster | 1 | 1x | 8.815 | 42.2 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_x101_vd_64x4d_fpn_1x.tar) | +| ResNeXt101-vd-64x4d-FPN | Faster | 1 | 2x | 8.809 | 41.7 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_x101_vd_64x4d_fpn_2x.tar) | +| ResNeXt101-vd-64x4d-FPN | Mask | 1 | 1x | 7.689 | 42.9 | 37.9 | [model](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_x101_vd_64x4d_fpn_1x.tar) | +| ResNeXt101-vd-64x4d-FPN | Mask | 1 | 2x | 7.859 | 42.6 | 37.6 | [model](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_x101_vd_64x4d_fpn_2x.tar) | +| SENet154-vd-FPN | Faster | 1 | 1.44x | 3.408 | 42.9 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_se154_vd_fpn_s1x.tar) | +| SENet154-vd-FPN | Mask | 1 | 1.44x | 3.233 | 44.0 | 38.7 | [model](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_se154_vd_fpn_s1x.tar) | + +### Deformable ConvNets v2 + +| Backbone | Type | Conv | Image/gpu | Lr schd | Inf time (fps) | Box AP | Mask AP | Download | +| :---------------------- | :------------- | :---: | :-------: | :-----: | :------------: | :----: | :-----: | :----------------------------------------------------------: | +| ResNet50-FPN | Faster | c3-c5 | 2 | 1x | 19.978 | 41.0 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_dcn_r50_fpn_1x.tar) | +| ResNet50-vd-FPN | Faster | c3-c5 | 2 | 2x | 19.222 | 42.4 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_dcn_r50_vd_fpn_2x.tar) | +| ResNet101-vd-FPN | Faster | c3-c5 | 2 | 1x | 14.477 | 44.1 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_dcn_r101_vd_fpn_1x.tar) | +| ResNeXt101-vd-64x4d-FPN | Faster | c3-c5 | 1 | 1x | 7.209 | 45.2 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_dcn_x101_vd_64x4d_fpn_1x.tar) | +| ResNet50-FPN | Mask | c3-c5 | 1 | 1x | 14.53 | 41.9 | 37.3 | [model](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_dcn_r50_fpn_1x.tar) | +| ResNet50-vd-FPN | Mask | c3-c5 | 1 | 2x | 14.832 | 42.9 | 38.0 | [model](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_dcn_r50_vd_fpn_2x.tar) | +| ResNet101-vd-FPN | Mask | c3-c5 | 1 | 1x | 11.546 | 44.6 | 39.2 | [model](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_dcn_r101_vd_fpn_1x.tar) | +| ResNeXt101-vd-64x4d-FPN | Mask | c3-c5 | 1 | 1x | 6.45 | 46.2 | 40.4 | [model](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_dcn_x101_vd_64x4d_fpn_1x.tar) | +| ResNet50-FPN | Cascade Faster | c3-c5 | 2 | 1x | - | 44.2 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/cascade_rcnn_dcn_r50_fpn_1x.tar) | +| ResNet101-vd-FPN | Cascade Faster | c3-c5 | 2 | 1x | - | 46.4 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/cascade_rcnn_dcn_r101_vd_fpn_1x.tar) | +| ResNeXt101-vd-FPN | Cascade Faster | c3-c5 | 2 | 1x | - | 47.3 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/cascade_rcnn_dcn_x101_vd_64x4d_fpn_1x.tar) | +| SENet154-vd-FPN | Cascade Mask | c3-c5 | 1 | 1.44x | - | 51.9 | 43.9 | [model](https://paddlemodels.bj.bcebos.com/object_detection/cascade_mask_rcnn_dcnv2_se154_vd_fpn_gn_s1x.tar) | + +#### Notes: +- Deformable ConvNets v2(dcn_v2) reference from [Deformable ConvNets v2](https://arxiv.org/abs/1811.11168). +- `c3-c5` means adding `dcn` in resnet stage 3 to 5. +- Detailed configuration file in [configs/dcn](https://github.com/PaddlePaddle/models/tree/develop/PaddleCV/PaddleDetection/configs/dcn) + +### Group Normalization +| Backbone | Type | Image/gpu | Lr schd | Box AP | Mask AP | Download | +| :------------------- | :------------- | :-----: | :-----: | :----: | :-----: | :----------------------------------------------------------: | +| ResNet50-FPN | Faster | 2 | 2x | 39.7 | - | [model](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_fpn_gn_2x.tar) | +| ResNet50-FPN | Mask | 1 | 2x | 40.1 | 35.8 | [model](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r50_fpn_gn_2x.tar) | + +#### Notes: +- Group Normalization reference from [Group Normalization](https://arxiv.org/abs/1803.08494). +- Detailed configuration file in [configs/gn](https://github.com/PaddlePaddle/models/tree/develop/PaddleCV/PaddleDetection/configs/gn) + +### Yolo v3 + +| Backbone | Size | Image/gpu | Lr schd | Inf time (fps) | Box AP | Download | +| :----------- | :--: | :-------: | :-----: | :------------: | :----: | :----------------------------------------------------------: | +| DarkNet53 | 608 | 8 | 270e | 45.571 | 38.9 | [model](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_darknet.tar) | +| DarkNet53 | 416 | 8 | 270e | - | 37.5 | [model](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_darknet.tar) | +| DarkNet53 | 320 | 8 | 270e | - | 34.8 | [model](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_darknet.tar) | +| MobileNet-V1 | 608 | 8 | 270e | 78.302 | 29.3 | [model](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1.tar) | +| MobileNet-V1 | 416 | 8 | 270e | - | 29.3 | [model](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1.tar) | +| MobileNet-V1 | 320 | 8 | 270e | - | 27.1 | [model](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1.tar) | +| ResNet34 | 608 | 8 | 270e | 63.356 | 36.2 | [model](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_r34.tar) | +| ResNet34 | 416 | 8 | 270e | - | 34.3 | [model](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_r34.tar) | +| ResNet34 | 320 | 8 | 270e | - | 31.4 | [model](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_r34.tar) | + + +### Yolo v3 on Pascal VOC + +| Backbone | Size | Image/gpu | Lr schd | Inf time (fps) | Box AP | Download | +| :----------- | :--: | :-------: | :-----: | :------------: | :----: | :----------------------------------------------------------: | +| DarkNet53 | 608 | 8 | 270e | 54.977 | 83.5 | [model](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_darknet_voc.tar) | +| DarkNet53 | 416 | 8 | 270e | - | 83.6 | [model](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_darknet_voc.tar) | +| DarkNet53 | 320 | 8 | 270e | - | 82.2 | [model](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_darknet_voc.tar) | +| MobileNet-V1 | 608 | 8 | 270e | 104.291 | 76.2 | [model](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1_voc.tar) | +| MobileNet-V1 | 416 | 8 | 270e | - | 76.7 | [model](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1_voc.tar) | +| MobileNet-V1 | 320 | 8 | 270e | - | 75.3 | [model](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1_voc.tar) | +| ResNet34 | 608 | 8 | 270e | 82.247 | 82.6 | [model](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_r34_voc.tar) | +| ResNet34 | 416 | 8 | 270e | - | 81.9 | [model](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_r34_voc.tar) | +| ResNet34 | 320 | 8 | 270e | - | 80.1 | [model](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_r34_voc.tar) | + +**Notes:** Yolo v3 is trained in 8 GPU with total batch size as 64 and trained 270 epoches. Yolo v3 training data augmentations: mixup, +randomly color distortion, randomly cropping, randomly expansion, randomly interpolation method, randomly flippling. Yolo v3 used randomly +reshaped minibatch in training, inferences can be performed on different image sizes with the same model weights, and we provided evaluation +results of image size 608/416/320 above. + +### RetinaNet + +| Backbone | Image/gpu | Lr schd | Box AP | Download | +| :---------------: | :-----: | :-----: | :----: | :-------: | +| ResNet50-FPN | 2 | 1x | 36.0 | [model](https://paddlemodels.bj.bcebos.com/object_detection/retinanet_r50_fpn_1x.tar) | +| ResNet101-FPN | 2 | 1x | 37.3 | [model](https://paddlemodels.bj.bcebos.com/object_detection/retinanet_r101_fpn_1x.tar) | +| ResNeXt101-vd-FPN | 1 | 1x | 40.5 | [model](https://paddlemodels.bj.bcebos.com/object_detection/retinanet_x101_vd_64x4d_fpn_1x.tar) | + +**Notes:** In RetinaNet, the base LR is changed to 0.01 for minibatch size 16. + +### SSD + +| Backbone | Size | Image/gpu | Lr schd | Inf time (fps) | Box AP | Download | +| :------: | :--: | :-------: | :-----: | :------------: | :----: | :----------------------------------------------------------: | +| VGG16 | 300 | 8 | 40w | 81.613 | 25.1 | [model](https://paddlemodels.bj.bcebos.com/object_detection/ssd_vgg16_300.tar) | +| VGG16 | 512 | 8 | 40w | 46.007 | 29.1 | [model](https://paddlemodels.bj.bcebos.com/object_detection/ssd_vgg16_512.tar) | + +**Notes:** VGG-SSD is trained in 4 GPU with total batch size as 32 and trained 400000 iters. + +### SSD on Pascal VOC + +| Backbone | Size | Image/gpu | Lr schd | Inf time (fps) | Box AP | Download | +| :----------- | :--: | :-------: | :-----: | :------------: | :----: | :----------------------------------------------------------: | +| MobileNet v1 | 300 | 32 | 120e | 159.543 | 73.2 | [model](https://paddlemodels.bj.bcebos.com/object_detection/ssd_mobilenet_v1_voc.tar) | +| VGG16 | 300 | 8 | 240e | 117.279 | 77.5 | [model](https://paddlemodels.bj.bcebos.com/object_detection/ssd_vgg16_300_voc.tar) | +| VGG16 | 512 | 8 | 240e | 65.975 | 80.2 | [model](https://paddlemodels.bj.bcebos.com/object_detection/ssd_vgg16_512_voc.tar) | + +**NOTE**: MobileNet-SSD is trained in 2 GPU with totoal batch size as 64 and trained 120 epoches. VGG-SSD is trained in 4 GPU with total batch size as 32 and trained 240 epoches. SSD training data augmentations: randomly color distortion, +randomly cropping, randomly expansion, randomly flipping. + + +## Face Detection + +Please refer [face detection models](../configs/face_detection) for details. diff --git a/docs/MODEL_ZOO_cn.md b/docs/MODEL_ZOO_cn.md new file mode 100644 index 0000000000000000000000000000000000000000..918a9cb249d105d9ed7cd0411ede8c5e8ce0c3d5 --- /dev/null +++ b/docs/MODEL_ZOO_cn.md @@ -0,0 +1,156 @@ +# 模型库和基线 + +## 测试环境 + +- Python 2.7.1 +- PaddlePaddle >=1.5 +- CUDA 9.0 +- cuDNN >=7.4 +- NCCL 2.1.2 + +## 通用设置 + +- 所有模型均在COCO17数据集中训练和测试。 +- 除非特殊说明,所有ResNet骨干网络采用[ResNet-B](https://arxiv.org/pdf/1812.01187)结构。 +- 对于RCNN和RetinaNet系列模型,训练阶段仅使用水平翻转作为数据增强,测试阶段不使用数据增强。 +- **推理时间(fps)**: 推理时间是在一张Tesla V100的GPU上通过'tools/eval.py'测试所有验证集得到,单位是fps(图片数/秒), cuDNN版本是7.5,包括数据加载、网络前向执行和后处理, batch size是1。 + +## 训练策略 + +- 我们采用和[Detectron](https://github.com/facebookresearch/Detectron/blob/master/MODEL_ZOO.md#training-schedules)相同的训练策略。 +- 1x 策略表示:在总batch size为16时,初始学习率为0.02,在6万轮和8万轮后学习率分别下降10倍,最终训练9万轮。在总batch size为8时,初始学习率为0.01,在12万轮和16万轮后学习率分别下降10倍,最终训练18万轮。 +- 2x 策略为1x策略的两倍,同时学习率调整位置也为1x的两倍。 + +## ImageNet预训练模型 + +Paddle提供基于ImageNet的骨架网络预训练模型。所有预训练模型均通过标准的Imagenet-1k数据集训练得到。[下载链接](https://github.com/PaddlePaddle/models/tree/develop/PaddleCV/image_classification#supported-models-and-performances) + +- 注:ResNet50模型通过余弦学习率调整策略训练得到。[下载链接](https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_cos_pretrained.tar) + +## 基线 + +### Faster & Mask R-CNN + +| 骨架网络 | 网络类型 | 每张GPU图片个数 | 学习率策略 |推理时间(fps) | Box AP | Mask AP | 下载 | +| :------------------- | :------------- | :-----: | :-----: | :------------: | :-----: | :-----: | :-----------------------------------------------------: | +| ResNet50 | Faster | 1 | 1x | 12.747 | 35.2 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_1x.tar) | +| ResNet50 | Faster | 1 | 2x | 12.686 | 37.1 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_2x.tar) | +| ResNet50 | Mask | 1 | 1x | 11.615 | 36.5 | 32.2 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r50_1x.tar) | +| ResNet50 | Mask | 1 | 2x | 11.494 | 38.2 | 33.4 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r50_2x.tar) | +| ResNet50-vd | Faster | 1 | 1x | 12.575 | 36.4 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_vd_1x.tar) | +| ResNet50-FPN | Faster | 2 | 1x | 22.273 | 37.2 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_fpn_1x.tar) | +| ResNet50-FPN | Faster | 2 | 2x | 22.297 | 37.7 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_fpn_2x.tar) | +| ResNet50-FPN | Mask | 1 | 1x | 15.184 | 37.9 | 34.2 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r50_fpn_1x.tar) | +| ResNet50-FPN | Mask | 1 | 2x | 15.881 | 38.7 | 34.7 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r50_fpn_2x.tar) | +| ResNet50-FPN | Cascade Faster | 2 | 1x | 17.507 | 40.9 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/cascade_rcnn_r50_fpn_1x.tar) | +| ResNet50-FPN | Cascade Mask | 1 | 1x | - | 41.3 | 35.5 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/cascade_mask_rcnn_r50_fpn_1x.tar) | +| ResNet50-vd-FPN | Faster | 2 | 2x | 21.847 | 38.9 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_vd_fpn_2x.tar) | +| ResNet50-vd-FPN | Mask | 1 | 2x | 15.825 | 39.8 | 35.4 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r50_vd_fpn_2x.tar) | +| ResNet101 | Faster | 1 | 1x | 9.316 | 38.3 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r101_1x.tar) | +| ResNet101-FPN | Faster | 1 | 1x | 17.297 | 38.7 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r101_fpn_1x.tar) | +| ResNet101-FPN | Faster | 1 | 2x | 17.246 | 39.1 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r101_fpn_2x.tar) | +| ResNet101-FPN | Mask | 1 | 1x | 12.983 | 39.5 | 35.2 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r101_fpn_1x.tar) | +| ResNet101-vd-FPN | Faster | 1 | 1x | 17.011 | 40.5 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r101_vd_fpn_1x.tar) | +| ResNet101-vd-FPN | Faster | 1 | 2x | 16.934 | 40.8 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r101_vd_fpn_2x.tar) | +| ResNet101-vd-FPN | Mask | 1 | 1x | 13.105 | 41.4 | 36.8 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r101_vd_fpn_1x.tar) | +| ResNeXt101-vd-FPN | Faster | 1 | 1x | 8.815 | 42.2 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_x101_vd_64x4d_fpn_1x.tar) | +| ResNeXt101-vd-FPN | Faster | 1 | 2x | 8.809 | 41.7 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_x101_vd_64x4d_fpn_2x.tar) | +| ResNeXt101-vd-FPN | Mask | 1 | 1x | 7.689 | 42.9 | 37.9 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_x101_vd_64x4d_fpn_1x.tar) | +| ResNeXt101-vd-FPN | Mask | 1 | 2x | 7.859 | 42.6 | 37.6 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_x101_vd_64x4d_fpn_2x.tar) | +| SENet154-vd-FPN | Faster | 1 | 1.44x | 3.408 | 42.9 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_se154_vd_fpn_s1x.tar) | +| SENet154-vd-FPN | Mask | 1 | 1.44x | 3.233 | 44.0 | 38.7 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_se154_vd_fpn_s1x.tar) | + +### Deformable 卷积网络v2 + +| 骨架网络 | 网络类型 | 卷积 | 每张GPU图片个数 | 学习率策略 |推理时间(fps)| Box AP | Mask AP | 下载 | +| :------------------- | :------------- | :-----: |:--------: | :-----: | :-----------: |:----: | :-----: | :----------------------------------------------------------: | +| ResNet50-FPN | Faster | c3-c5 | 2 | 1x | 19.978 | 41.0 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_dcn_r50_fpn_1x.tar) | +| ResNet50-vd-FPN | Faster | c3-c5 | 2 | 2x | 19.222 | 42.4 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_dcn_r50_vd_fpn_2x.tar) | +| ResNet101-vd-FPN | Faster | c3-c5 | 2 | 1x | 14.477 | 44.1 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_dcn_r101_vd_fpn_1x.tar) | +| ResNeXt101-vd-FPN | Faster | c3-c5 | 1 | 1x | 7.209 | 45.2 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_dcn_x101_vd_64x4d_fpn_1x.tar) | +| ResNet50-FPN | Mask | c3-c5 | 1 | 1x | 14.53 | 41.9 | 37.3 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_dcn_r50_fpn_1x.tar) | +| ResNet50-vd-FPN | Mask | c3-c5 | 1 | 2x | 14.832 | 42.9 | 38.0 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_dcn_r50_vd_fpn_2x.tar) | +| ResNet101-vd-FPN | Mask | c3-c5 | 1 | 1x | 11.546 | 44.6 | 39.2 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_dcn_r101_vd_fpn_1x.tar) | +| ResNeXt101-vd-FPN | Mask | c3-c5 | 1 | 1x | 6.45 | 46.2 | 40.4 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_dcn_x101_vd_64x4d_fpn_1x.tar) | +| ResNet50-FPN | Cascade Faster | c3-c5 | 2 | 1x | - | 44.2 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/cascade_rcnn_dcn_r50_fpn_1x.tar) | +| ResNet101-vd-FPN | Cascade Faster | c3-c5 | 2 | 1x | - | 46.4 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/cascade_rcnn_dcn_r101_vd_fpn_1x.tar) | +| ResNeXt101-vd-FPN | Cascade Faster | c3-c5 | 2 | 1x | - | 47.3 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/cascade_rcnn_dcn_x101_vd_64x4d_fpn_1x.tar) | +| SENet154-vd-FPN | Cascade Mask | c3-c5 | 1 | 1.44x | - | 51.9 | 43.9 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/cascade_mask_rcnn_dcnv2_se154_vd_fpn_gn_s1x.tar) | + +#### 注意事项: +- Deformable卷积网络v2(dcn_v2)参考自论文[Deformable ConvNets v2](https://arxiv.org/abs/1811.11168). +- `c3-c5`意思是在resnet模块的3到5阶段增加`dcn`. +- 详细的配置文件在[configs/dcn](https://github.com/PaddlePaddle/models/tree/develop/PaddleCV/PaddleDetection/configs/dcn) + +### Group Normalization +| 骨架网络 | 网络类型 | 每张GPU图片个数 | 学习率策略 | Box AP | Mask AP | 下载 | +| :------------------- | :------------- |:--------: | :-----: | :----: | :-----: | :----------------------------------------------------------: | +| ResNet50-FPN | Faster | 2 | 2x | 39.7 | - | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_fpn_gn_2x.tar) | +| ResNet50-FPN | Mask | 1 | 2x | 40.1 | 35.8 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/mask_rcnn_r50_fpn_gn_2x.tar) | + +#### 注意事项: +- Group Normalization参考论文[Group Normalization](https://arxiv.org/abs/1803.08494). +- 详细的配置文件在[configs/gn](https://github.com/PaddlePaddle/models/tree/develop/PaddleCV/PaddleDetection/configs/gn) + +### Yolo v3 + +| 骨架网络 | 输入尺寸 | 每张GPU图片个数 | 学习率策略 |推理时间(fps)| Box AP | 下载 | +| :----------- | :--: | :-----: | :-----: |:------------: |:----: | :-------: | +| DarkNet53 | 608 | 8 | 270e | 45.571 | 38.9 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_darknet.tar) | +| DarkNet53 | 416 | 8 | 270e | - | 37.5 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_darknet.tar) | +| DarkNet53 | 320 | 8 | 270e | - | 34.8 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_darknet.tar) | +| MobileNet-V1 | 608 | 8 | 270e | 78.302 | 29.3 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1.tar) | +| MobileNet-V1 | 416 | 8 | 270e | - | 29.3 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1.tar) | +| MobileNet-V1 | 320 | 8 | 270e | - | 27.1 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1.tar) | +| ResNet34 | 608 | 8 | 270e | 63.356 | 36.2 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_r34.tar) | +| ResNet34 | 416 | 8 | 270e | - | 34.3 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_r34.tar) | +| ResNet34 | 320 | 8 | 270e | - | 31.4 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_r34.tar) | + +### Yolo v3 基于Pasacl VOC数据集 + +| 骨架网络 | 输入尺寸 | 每张GPU图片个数 | 学习率策略 |推理时间(fps)| Box AP | 下载 | +| :----------- | :--: | :-----: | :-----: |:------------: |:----: | :-------: | +| DarkNet53 | 608 | 8 | 270e | 54.977 | 83.5 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_darknet_voc.tar) | +| DarkNet53 | 416 | 8 | 270e | - | 83.6 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_darknet_voc.tar) | +| DarkNet53 | 320 | 8 | 270e | - | 82.2 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_darknet_voc.tar) | +| MobileNet-V1 | 608 | 8 | 270e | 104.291 | 76.2 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1_voc.tar) | +| MobileNet-V1 | 416 | 8 | 270e | - | 76.7 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1_voc.tar) | +| MobileNet-V1 | 320 | 8 | 270e | - | 75.3 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1_voc.tar) | +| ResNet34 | 608 | 8 | 270e | 82.247 | 82.6 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_r34_voc.tar) | +| ResNet34 | 416 | 8 | 270e | - | 81.9 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_r34_voc.tar) | +| ResNet34 | 320 | 8 | 270e | - | 80.1 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_r34_voc.tar) | + +**注意事项:** Yolo v3在8卡,总batch size为64下训练270轮。数据增强包括:mixup, 随机颜色失真,随机剪裁,随机扩张,随机插值法,随机翻转。Yolo v3在训练阶段对minibatch采用随机reshape,可以采用相同的模型测试不同尺寸图片,我们分别提供了尺寸为608/416/320大小的测试结果。 + +### RetinaNet + +| 骨架网络 | 每张GPU图片个数 | 学习率策略 | Box AP | 下载 | +| :---------------: | :-----: | :-----: | :----: | :-------: | +| ResNet50-FPN | 2 | 1x | 36.0 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/retinanet_r50_fpn_1x.tar) | +| ResNet101-FPN | 2 | 1x | 37.3 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/retinanet_r101_fpn_1x.tar) | +| ResNeXt101-vd-FPN | 1 | 1x | 40.5 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/retinanet_x101_vd_64x4d_fpn_1x.tar) | + +**注意事项:** RetinaNet系列模型中,在总batch size为16下情况下,初始学习率改为0.01。 + +### SSD + +| 骨架网络 | 输入尺寸 | 每张GPU图片个数 | 学习率策略|推理时间(fps) | Box AP | 下载 | +| :----------: | :--: | :-----: | :-----: |:------------: |:----: | :-------: | +| VGG16 | 300 | 8 | 40万 | 81.613 | 25.1 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/ssd_vgg16_300.tar) | +| VGG16 | 512 | 8 | 40万 | 46.007 | 29.1 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/ssd_vgg16_512.tar) | + +**注意事项:** VGG-SSD在总batch size为32下训练40万轮。 + +### SSD 基于Pascal VOC数据集 + +| 骨架网络 | 输入尺寸 | 每张GPU图片个数 | 学习率策略 |推理时间(fps)| Box AP | 下载 | +| :----------- | :--: | :-----: | :-----: | :------------: |:----: | :-------: | +| MobileNet v1 | 300 | 32 | 120e | 159.543 | 73.2 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/ssd_mobilenet_v1_voc.tar) | +| VGG16 | 300 | 8 | 240e | 117.279 | 77.5 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/ssd_vgg16_300_voc.tar) | +| VGG16 | 512 | 8 | 240e | 65.975 | 80.2 | [下载链接](https://paddlemodels.bj.bcebos.com/object_detection/ssd_vgg16_512_voc.tar) | + +**注意事项:** MobileNet-SSD在2卡,总batch size为64下训练120周期。VGG-SSD在总batch size为32下训练240周期。数据增强包括:随机颜色失真,随机剪裁,随机扩张,随机翻转。 + +## 人脸检测 + +详细请参考[人脸检测模型](../configs/face_detection). diff --git a/docs/QUICK_STARTED.md b/docs/QUICK_STARTED.md new file mode 100644 index 0000000000000000000000000000000000000000..c6649f71f7b41c2fda00141e20c16dffc1f93e40 --- /dev/null +++ b/docs/QUICK_STARTED.md @@ -0,0 +1,67 @@ +English | [简体中文](QUICK_STARTED_cn.md) + +# Quick Start + +This tutorial fine-tunes a tiny dataset by pretrained detection model for users to get a model and learn PaddleDetection quickly. The model can be trained in around 20min with good performance. + +## Data Preparation + +Dataset refers to [Kaggle](https://www.kaggle.com/mbkinaci/fruit-images-for-object-detection), which contains 240 images in train dataset and 60 images in test dataset. Data categories are apple, orange and banana. Download [here](https://dataset.bj.bcebos.com/PaddleDetection_demo/fruit-detection.tar) and uncompress the dataset after download, script for data preparation is located at [download_fruit.py](../dataset/fruit/download_fruit.py). Command is as follows: + +```bash +export PYTHONPATH=$PYTHONPATH:. +python dataset/fruit/download_fruit.py +``` + +- **Note: before started, run the following command and specifiy the GPU** + +```bash +export PYTHONPATH=$PYTHONPATH:. +export CUDA_VISIBLE_DEVICES=0 +``` + +Training: + +```bash +python -u tools/train.py -c configs/yolov3_mobilenet_v1_fruit.yml \ + --use_tb=True \ + --tb_log_dir=tb_fruit_dir/scalar \ + --eval \ +``` + +Use `yolov3_mobilenet_v1` to fine-tune the model from COCO dataset. Meanwhile, loss and mAP can be observed on tensorboard. + +```bash +tensorboard --logdir tb_fruit_dir/scalar/ --host --port +``` + +Result on tensorboard is shown below: + +
+ +
+ +Model can be downloaded [here](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1_fruit.tar) + +Evaluation: + +```bash +python -u tools/eval.py -c configs/yolov3_mobilenet_v1_fruit.yml +``` + +Inference: + +```bash +python -u tools/infer.py -c configs/yolov3_mobilenet_v1_fruit.yml \ + -o weights=https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1_fruit.tar \ + --infer_img=demo/orange_71.jpg +``` + +Inference images are shown below: + +

+ + +

+ +For detailed infomation of training and evalution, please refer to [GETTING_STARTED.md](GETTING_STARTED.md). diff --git a/docs/QUICK_STARTED_cn.md b/docs/QUICK_STARTED_cn.md new file mode 100644 index 0000000000000000000000000000000000000000..8c02ffb798250a0fa29db02ab7e4b38f04e4daac --- /dev/null +++ b/docs/QUICK_STARTED_cn.md @@ -0,0 +1,67 @@ +[English](QUICK_STARTED.md) | 简体中文 + +# 快速开始 + +为了使得用户能够在很短的时间内快速产出模型,掌握PaddleDetection的使用方式,这篇教程通过一个预训练检测模型对小数据集进行finetune。在P40上单卡大约20min即可产出一个效果不错的模型。 + +## 数据准备 + +数据集参考[Kaggle数据集](https://www.kaggle.com/mbkinaci/fruit-images-for-object-detection),其中训练数据集240张图片,测试数据集60张图片,数据类别为3类:苹果,橘子,香蕉。[下载链接](https://dataset.bj.bcebos.com/PaddleDetection_demo/fruit-detection.tar)。数据下载后分别解压即可, 数据准备脚本位于[download_fruit.py](../dataset/fruit/download_fruit.py)。下载数据方式如下: + +```bash +export PYTHONPATH=$PYTHONPATH:. +python dataset/fruit/download_fruit.py +``` + +- **注:在开始前,运行如下命令并指定GPU** + +```bash +export PYTHONPATH=$PYTHONPATH:. +export CUDA_VISIBLE_DEVICES=0 +``` + +训练命令如下: + +```bash +python -u tools/train.py -c configs/yolov3_mobilenet_v1_fruit.yml \ + --use_tb=True \ + --tb_log_dir=tb_fruit_dir/scalar \ + --eval \ +``` + +训练使用`yolov3_mobilenet_v1`基于COCO数据集训练好的模型进行finetune。训练期间可以通过tensorboard实时观察loss和精度值,启动命令如下: + +```bash +tensorboard --logdir tb_fruit_dir/scalar/ --host --port +``` + +tensorboard结果显示如下: + +
+ +
+ +训练模型[下载链接](https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1_fruit.tar) + +评估命令如下: + +```bash +python -u tools/eval.py -c configs/yolov3_mobilenet_v1_fruit.yml +``` + +预测命令如下 + +```bash +python -u tools/infer.py -c configs/yolov3_mobilenet_v1_fruit.yml \ + -o weights=https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1_fruit.tar \ + --infer_img=demo/orange_71.jpg +``` + +预测图片如下: + +

+ + +

+ +更多训练及评估流程,请参考[GETTING_STARTED_cn.md](GETTING_STARTED_cn.md). diff --git a/docs/TRANSFER_LEARNING.md b/docs/TRANSFER_LEARNING.md new file mode 100644 index 0000000000000000000000000000000000000000..0bc0377acb749ee896050660ba122a3a77ca20b7 --- /dev/null +++ b/docs/TRANSFER_LEARNING.md @@ -0,0 +1,39 @@ +English | [简体中文](TRANSFER_LEARNING_cn.md) + +# Transfer Learning + +Transfer learning aims at learning new knowledge from existing knowledge. For example, take pretrained model from ImageNet to initialize detection models, or take pretrained model from COCO dataset to initialize train detection models in PascalVOC dataset. + +In transfer learning, if different dataset and the number of classes is used, the dimensional inconsistency will causes in loading parameters related to the number of classes; On the other hand, if more complicated model is used, need to motify the open-source model construction and selective load parameters. Thus, PaddleDetection should designate parameter fields and ignore loading the parameters which match the fields. + +## Transfer Learning in PaddleDetection + +In transfer learning, it's needed to load pretrained model selectively. The following two methods can be used: + +1. Set `finetune_exclude_pretrained_params` in YAML configuration files. Please refer to [configure file](../configs/yolov3_mobilenet_v1_fruit.yml#L15) +2. Set -o finetune_exclude_pretrained_params in command line. For example: + +```python +export PYTHONPATH=$PYTHONPATH:. +export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 +python -u tools/train.py -c configs/faster_rcnn_r50_1x.yml \ + -o pretrain_weights=https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_1x.tar \ + finetune_exclude_pretrained_params=['cls_score','bbox_pred'] +``` + +* Note: + +1. The path in pretrain\_weights is the open-source model link of faster RCNN from COCO dataset. For full models link, please refer to [MODEL_ZOO](MODEL_ZOO.md) +2. The parameter fields are set in finetune\_exclude\_pretrained\_params. If the name of parameter matches field (wildcard matching), the parameter will be ignored in loading. + +If users want to fine-tune by own dataet, and remain the model construction, need to ignore the parameters related to the number of classes. PaddleDetection lists ignored parameter fields corresponding to different model type. The table is shown below:
+ +| model type | ignored parameter fields | +| :----------------: | :---------------------------------------: | +| Faster RCNN | cls\_score, bbox\_pred | +| Cascade RCNN | cls\_score, bbox\_pred | +| Mask RCNN | cls\_score, bbox\_pred, mask\_fcn\_logits | +| Cascade-Mask RCNN | cls\_score, bbox\_pred, mask\_fcn\_logits | +| RetinaNet | retnet\_cls\_pred\_fpn | +| SSD | ^conv2d\_ | +| YOLOv3 | yolo\_output | diff --git a/docs/TRANSFER_LEARNING_cn.md b/docs/TRANSFER_LEARNING_cn.md new file mode 100644 index 0000000000000000000000000000000000000000..a54210d0aa9ef32096620e1830d49a2b2430b189 --- /dev/null +++ b/docs/TRANSFER_LEARNING_cn.md @@ -0,0 +1,37 @@ +# 迁移学习 + +迁移学习为利用已有知识,对新知识进行学习。例如利用ImageNet分类预训练模型做初始化来训练检测模型,利用在COCO数据集上的检测模型做初始化来训练基于PascalVOC数据集的检测模型。 + +在进行迁移学习时,由于会使用不同的数据集,数据类别数与COCO/VOC数据类别不同,导致在加载PaddlePaddle开源模型时,与类别数相关的权重(例如分类模块的fc层)会出现维度不匹配的问题;另外,如果需要结构更加复杂的模型,需要对已有开源模型结构进行调整,对应权重也需要选择性加载。因此,需要检测库能够指定参数字段,在加载模型时不加载匹配的权重。 + +## PaddleDetection进行迁移学习 + +在迁移学习中,对预训练模型进行选择性加载,可通过如下两种方式实现: + +1. 在 YMAL 配置文件中通过设置`finetune_exclude_pretrained_params`字段。可参考[配置文件](../configs/yolov3_mobilenet_v1_fruit.yml#L15) +2. 在 train.py的启动参数中设置 -o finetune_exclude_pretrained_params。例如: + +```python +export PYTHONPATH=$PYTHONPATH:. +export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7 +python -u tools/train.py -c configs/faster_rcnn_r50_1x.yml \ + -o pretrain_weights=https://paddlemodels.bj.bcebos.com/object_detection/faster_rcnn_r50_1x.tar \ + finetune_exclude_pretrained_params=['cls_score','bbox_pred'] +``` + +* 说明: + +1. pretrain\_weights的路径为COCO数据集上开源的faster RCNN模型链接,完整模型链接可参考[MODEL_ZOO](MODEL_ZOO_cn.md) +2. finetune\_exclude\_pretrained\_params中设置参数字段,如果参数名能够匹配以上参数字段(通配符匹配方式),则在模型加载时忽略该参数。 + +如果用户需要利用自己的数据进行finetune,模型结构不变,只需要忽略与类别数相关的参数。PaddleDetection给出了不同模型类型所对应的忽略参数字段。如下表所示:
+ +| 模型类型 | 忽略参数字段 | +| :----------------: | :---------------------------------------: | +| Faster RCNN | cls\_score, bbox\_pred | +| Cascade RCNN | cls\_score, bbox\_pred | +| Mask RCNN | cls\_score, bbox\_pred, mask\_fcn\_logits | +| Cascade-Mask RCNN | cls\_score, bbox\_pred, mask\_fcn\_logits | +| RetinaNet | retnet\_cls\_pred\_fpn | +| SSD | ^conv2d\_ | +| YOLOv3 | yolo\_output | diff --git a/docs/config_example/mask_rcnn_r50_fpn_1x.yml b/docs/config_example/mask_rcnn_r50_fpn_1x.yml new file mode 100644 index 0000000000000000000000000000000000000000..50d38777a565e8b58e3d8dd4d40279e733bc3b1a --- /dev/null +++ b/docs/config_example/mask_rcnn_r50_fpn_1x.yml @@ -0,0 +1,498 @@ +# Architecture of detection, which is also the prefix of data feed module +architecture: MaskRCNN + +# Data feed module +train_feed: MaskRCNNTrainFeed +eval_feed: MaskRCNNEvalFeed +test_feed: MaskRCNNTestFeed + +# Use GPU or CPU, true by default +use_gpu: true + +# Maximum number of iteration. +# In rcnn models, max_iters is 180000 if lr schedule is 1x and batch_size is 1. +max_iters: 180000 + +# Snapshot period. If training and test at same time, evaluate model at each snapshot_iter. 10000 by default. +snapshot_iter: 10000 + +# Smooth the log output in specified iterations, 20 by default. +log_smooth_window: 20 + +# The number of iteration interval to display in training log. +log_iter: 20 + +# The directory to save models. +save_dir: output + +# The path of oretrained wegiths. If url is provided, it will download the pretrain_weights and decompress automatically. +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/ResNet50_cos_pretrained.tar + +# Evalution method, COCO and VOC are available. +metric: COCO + +# The path of final model for evaluation and test. +weights: output/mask_rcnn_r50_fpn_1x/model_final/ + +# Number of classes, 81 for COCO and 21 for VOC +num_classes: 81 + +# Mask RCNN architecture, see https://arxiv.org/abs/1703.06870 +MaskRCNN: + backbone: ResNet + fpn: FPN + roi_extractor: FPNRoIAlign + rpn_head: FPNRPNHead + bbox_assigner: BBoxAssigner + bbox_head: BBoxHead + mask_assigner: MaskAssigner + mask_head: MaskHead + rpn_only: false + +# Backbone module +ResNet: + # Index of stages using deformable conv v2, [] by default + dcn_v2_stages: [] + # ResNet depth, 50 by default + depth: 50 + # Stage index of returned feature map, [2,3,4,5] by default + feature_maps: + - 2 + - 3 + - 4 + - 5 + # Stage Index of backbone to freeze, 2 by default + freeze_at: 2 + # Whether freeze normalization layers, true by default + freeze_norm: true + # Weight decay for normalization layer weights, 0. by default + norm_decay: 0.0 + # Normalization type, bn/sync_bn/affine_channel, affine_channel by default + norm_type: affine_channel + # ResNet variant, supports 'a', 'b', 'c', 'd' currently, b by default + variant: b + +# FPN module +FPN: + # Whether has extra conv in higher levels, false by default + has_extra_convs: false + # Highest level of the backbone feature map to use, 6 by default + max_level: 6 + # Lowest level of the backbone feature map to use, 6 by default + min_level: 2 + # FPN normalization type, bn/sync_bn/affine_channel, null by default + norm_type: null + # Number of feature channels, 256 by default + num_chan: 256 + # Feature map scaling factors, [0.03125, 0.0625, 0.125, 0.25] by default + spatial_scale: + - 0.03125 + - 0.0625 + - 0.125 + - 0.25 + +# RPN module, if use non-FPN architecture, use RPNHead instead +# Extract proposals according to anchors and assign box targets and +# score targets to selected proposals to compute RPN loss. For FPN +# architecture, RPN is computed from each levels and collect proposals +# together. +FPNRPNHead: + # fluid.layers.anchor_generator + # Generate anchors for RCNN models. Each position of input produces + # N anchors. N = anchor_sizes * aspect_ratios. In FPNRPNHead, aspect_ratios + # is provided and anchor_sizes depends on FPN levels and anchor_start_size. + anchor_generator: + aspect_ratios: + - 0.5 + - 1.0 + - 2.0 + variance: + - 1.0 + - 1.0 + - 1.0 + - 1.0 + # fluid.layers.rpn_target_assign + # Assign classification and regression targets to each anchor according + # to Intersection-over-Union(IoU) overlap between anchors and ground + # truth boxes. The classification targets is binary class labels. the + # positive labels are two kinds of anchors: the anchors with the highest + # IoU overlap with a ground-truth box, or an anchor that has an IoU overlap + # higher than rpn_positive_overlap with any ground-truth box. + 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 + # fluid.layers.generate_proposals in training + # Generate RoIs according to each box with probability to be a foreground + # object. The operation performs following steps: Transposes and resizes + # scores and bbox_deltas; Calculate box locations as proposal candidates; + # Clip boxes to image; Remove predicted boxes with small area; Apply NMS to + # get final proposals as output. + train_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 2000 + pre_nms_top_n: 2000 + # fluid.layers.generate_proposals in test + test_proposal: + min_size: 0.0 + nms_thresh: 0.7 + post_nms_top_n: 1000 + pre_nms_top_n: 1000 + # Size of anchor at the first scale, 32 by default + anchor_start_size: 32 + # highest level of FPN output, 6 by default + max_level: 6 + # Lowest level of FPN output, 2 by default + min_level: 2 + # Number of FPN output channels, 256 by default + num_chan: 256 + # Number of classes in RPN output, 1 by default + num_classes: 1 + +# RoI extractor module, if use non-FPN architecture, use RoIAlign instead +# For FPN architecture, proposals are distributed to different levels and +# apply roi align at each level. Then concat the outputs. +FPNRoIAlign: + # The canconical FPN feature map level, 4 by default + canconical_level: 4 + # The canconical FPN feature map size, 224 by default + canonical_size: 224 + # The highest level of FPN layer, 5 by default + max_level: 5 + # The lowest level of FPN layer, 2 by default + min_level: 2 + # Number of sampling points, 0 by default + sampling_ratio: 2 + # Box resolution, 7 by default + box_resolution: 7 + # Mask RoI resolution, 14 by default + mask_resolution: 14 + +# Mask head module +# Generate mask output and compute loss mask. +MaskHead: + # Number of convolutions, 4 for FPN, 0 otherwise. 0 by default + num_convs: 4 + # size of the output mask, 14 by default + resolution: 28 + # Dilation rate, 1 by default + dilation: 1 + # Number of channels after first conv, 256 by default + num_chan_reduced: 256 + # Number of output classes, 81 by default + num_classes: 81 + +# fluid.layers.generate_proposal_labels +# Combine boxes and gt_boxes, and sample foreground proposals and background +# prosals.Then assign classification and regression targets to selected RoIs. +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 + num_classes: 81 + shuffle_before_sample: true + +# fluid.layers.generate_mask_labels +# For given the RoIs and corresponding labels, sample foreground RoIs. +# Assign mask targets to selected RoIs which are encoded to K binary masks +# of resolution M x M. +MaskAssigner: + resolution: 28 + num_classes: 81 + +# BBox head module +# Faster bbox head following the RoI extractor, and apply post process, such as +# NMS and box coder.. +BBoxHead: + # Head after RoI extractor, ResNetC5/TwoFCHead + head: TwoFCHead + # fluid.layers.multiclass_nms + # Select a subset of detection bounding boxes that have high scores larger + # than score_threshold. Then prune away boxes that have high IoU overlap + # with already selected boxes by nms_threshold. + keep_top_k: 100 + nms_threshold: 0.5 + score_threshold: 0.05 + # fluid.layers.box_coder + box_coder: + axis: 1 + box_normalized: false + code_type: decode_center_size + prior_box_var: + - 0.1 + - 0.1 + - 0.2 + - 0.2 + num_classes: 81 + +# RCNN head with two Fully Connected layers +TwoFCHead: + # The number of output channels, 1024 by default + num_chan: 1024 + +# Learning rate configuration +LearningRate: + # Base learning rate, 0.01 by default + base_lr: 0.01 + # Learning rate schedulers, PiecewiseDecay and LinearWarmup by default + schedulers: + # fluid.layers.piecewise_decay + # Values has higher priority and if values is null, learning rate is multipled by gamma at each stage + - !PiecewiseDecay + gamma: 0.1 + milestones: + - 120000 + - 160000 + values: null + # fluid.layers.linear_lr_warmup + # Start learning rate equals to base_lr * start_factor + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 + +# Optimizer module +OptimizerBuilder: + # fluid.optimizer + optimizer: + momentum: 0.9 + type: Momentum + # fluid.regularizer + regularizer: + factor: 0.0001 + type: L2 + +# Data feed module for training +MaskRCNNTrainFeed: + # Batch size per device, 1 by default + batch_size: 1 + # Dataset module + dataset: + # Annotation file path + annotation: annotations/instances_train2017.json + # Dataset directory + dataset_dir: dataset/coco + # Directory where image files are stored + image_dir: train2017 + # List of data fields needed + fields: + - image + - im_info + - im_id + - gt_box + - gt_label + - is_crowd + - gt_mask + # list of image dims + image_shape: + - 3 + - 800 + - 1333 + # List of sample transformations to use + sample_transforms: + # Transform the image data to numpy format. + - !DecodeImage + to_rgb: true # default: true + with_mixup: false # default: false + # Flip images randomly + # Transform the x coordinates of bboxes and segmentations + - !RandomFlipImage + is_mask_flip: true # default: false + # Whether bbox is normalized + is_normalized: false # default: false + prob: 0.5 # default: 0.5 + # Normalize the image + - !NormalizeImage + # The format of image, [H, W, C]/[C, H, W], true by default + is_channel_first: false + # Whether divide by 255, true by default + is_scale: true + # default: [0.485, 0.456, 0.406] + mean: + - 0.485 + - 0.456 + - 0.406 + # default: [1, 1, 1] + std: + - 0.229 + - 0.224 + - 0.225 + # Rescale image to the specified target size, and capped at max_size + - !ResizeImage + # Resize method, cv2.INTER_LINEAR(1) by default + interp: 1 + max_size: 1333 + target_size: 800 + use_cv2: true # default: true + # Change the channel + - !Permute + # The format of image, [H, W, C]/[C, H, W], true by default + channel_first: true + to_bgr: false # default: true + # List of batch transformations to use + batch_transforms: + # Pad a batch of samples to same dimensions + - !PadBatch + pad_to_stride: 32 # default: 32 + # Drop last batch if size is uneven, false by default + drop_last: false + # Number of workers processes(or threads), 2 by default + num_workers: 2 + # Number of samples, -1 represents all samples. -1 by default + samples: -1 + # If samples should be shuffled, true by default + shuffle: true + # If update im_info after padding, false by default + use_padded_im_info: false + # If use multi-process, false by default + use_process: false + +# Data feed module for test +MaskRCNNEvalFeed: + # Batch size per device, 1 by default + batch_size: 1 + # Dataset module + dataset: + # Annotation file path + annotation: annotations/instances_val2017.json + # Dataset directory + dataset_dir: dataset/coco + # Directory where image files are stored + image_dir: val2017 + # List of data fields needed + fields: + - image + - im_info + - im_id + - im_shape + # list of image dims + image_shape: + - 3 + - 800 + - 1333 + # List of sample transformations to use + sample_transforms: + # Transform the image data to numpy format. + - !DecodeImage + to_rgb: true # default: true + with_mixup: false # default: false + # Normalize the image + - !NormalizeImage + # The format of image, [H, W, C]/[C, H, W], true by default + is_channel_first: false + # Whether divide by 255, true by default + is_scale: true + # default: [0.485, 0.456, 0.406] + mean: + - 0.485 + - 0.456 + - 0.406 + # default: [1, 1, 1] + std: + - 0.229 + - 0.224 + - 0.225 + # Rescale image to the specified target size, and capped at max_size + - !ResizeImage + # Resize method, cv2.INTER_LINEAR(1) by default + interp: 1 + max_size: 1333 + target_size: 800 + use_cv2: true # default: true + # Change the channel + - !Permute + # The format of image, [H, W, C]/[C, H, W], true by default + channel_first: true + to_bgr: false # default: true + # List of batch transformations to use + batch_transforms: + # Pad a batch of samples to same dimensions + - !PadBatch + pad_to_stride: 32 # default: 32 + # Drop last batch if size is uneven, false by default + drop_last: false + # Number of workers processes(or threads), 2 by default + num_workers: 2 + # Number of samples, -1 represents all samples. -1 by default + samples: -1 + # If samples should be shuffled, true by default + shuffle: false + # If update im_info after padding, false by default + use_padded_im_info: true + # If use multi-process, false by default + use_process: false + +# Data feed module for test +MaskRCNNTestFeed: + # Batch size per device, 1 by default + batch_size: 1 + # Dataset module + dataset: + # Annotation file path + annotation: dataset/coco/annotations/instances_val2017.json + # List of data fields needed + fields: + - image + - im_info + - im_id + - im_shape + # list of image dims + image_shape: + - 3 + - 800 + - 1333 + # List of sample transformations to use + sample_transforms: + # Transform the image data to numpy format. + - !DecodeImage + to_rgb: true # default: true + with_mixup: false # default: false + # Normalize the image + - !NormalizeImage + # The format of image, [H, W, C]/[C, H, W], true by default + is_channel_first: false + # Whether divide by 255, true by default + is_scale: true + # default: [0.485, 0.456, 0.406] + mean: + - 0.485 + - 0.456 + - 0.406 + # default: [1, 1, 1] + std: + - 0.229 + - 0.224 + - 0.225 + # Change the channel + - !Permute + # The format of image, [H, W, C]/[C, H, W], true by default + channel_first: true + to_bgr: false # default: true + # List of batch transformations to use + batch_transforms: + # Pad a batch of samples to same dimensions + - !PadBatch + pad_to_stride: 32 # default: 32 + # Drop last batch if size is uneven, false by default + drop_last: false + # Number of workers processes(or threads), 2 by default + num_workers: 2 + # Number of samples, -1 represents all samples. -1 by default + samples: -1 + # If samples should be shuffled, true by default + shuffle: false + # If update im_info after padding, false by default + use_padded_im_info: true + # If use multi-process, false by default + use_process: false + + diff --git a/docs/config_example/ssd_vgg16_300.yml b/docs/config_example/ssd_vgg16_300.yml new file mode 100644 index 0000000000000000000000000000000000000000..5a6dc7c210edc0ed2ac550a653463f3142dbc393 --- /dev/null +++ b/docs/config_example/ssd_vgg16_300.yml @@ -0,0 +1,427 @@ +# Architecture of detection, which is also the prefix of data feed module. +architecture: SSD +# Data feed module. +# Data feed in training. +train_feed: SSDTrainFeed +# Data feed in Evaluation. +eval_feed: SSDEvalFeed +# Data feed in infer. +test_feed: SSDTestFeed +# Use GPU or CPU, true by default. +use_gpu: true +# Maximum number of iteration. +max_iters: 400000 +# Snapshot period. If training and test at same time, evaluate model at each snapshot_iter. 10000 by default. +snapshot_iter: 10000 +# Smooth the log output in specified iterations, 20 by default. +log_smooth_window: 20 +# The log in training is displayed once every period. +log_iter: 20 +# Evaluation method, COCO and VOC are available. +metric: COCO +# Evaluation mAP calculation method in VOC metric, 11point and integral are available. +map_type: 11point +# The path of final model for evaluation and test. +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/VGG16_caffe_pretrained.tar +# The directory to save models. +save_dir: output +# The path of final model for evaluation and test. +weights: output/ssd_vgg16_300/model_final +# Number of classes, 81 for COCO and 21 for VOC. +num_classes: 81 + +# SSD architecture, see https://arxiv.org/abs/1512.02325 +SSD: + # backbone instance, defined below. + backbone: VGG + # `MultiBoxHead` instance, defined below. + multi_box_head: MultiBoxHead + + # fluid.layers.detection_output, Detection Output Layer for SSD. + # This operation is to get the detection results by performing following two steps: + # 1. Decode input bounding box predictions according to the prior boxes. + # 2. Get the final detection results by applying multi-class non maximum suppression (NMS). + # this operation doesn’t clip the final output bounding boxes to the image window. + output_decoder: + # The index of background label, the background label will be ignored. + # If set to -1, then all categories will be considered. + background_label: 0 + # Number of total bboxes to be kept per image after NMS. + keep_top_k: 200 + # The parameter for adaptive NMS. + nms_eta: 1.0 + # The threshold to be used in NMS. + nms_threshold: 0.45 + # Maximum number of detections to be kept according to the confidences + # aftern the filtering detections based on score_threshold. + nms_top_k: 400 + # Threshold to filter out bounding boxes with low confidence score. + # If not provided, consider all boxes. + score_threshold: 0.01 + +# VGG backbone, see https://arxiv.org/abs/1409.1556 +VGG: + # the VGG net depth (16 or 19 + depth: 16 + # whether or not extra blocks should be added + with_extra_blocks: true + # in each extra block, params: + # [in_channel, out_channel, padding_size, stride_size, filter_size] + extra_block_filters: + - [256, 512, 1, 2, 3] + - [128, 256, 1, 2, 3] + - [128, 256, 0, 1, 3] + - [128, 256, 0, 1, 3] + # params list of init scale in l2 norm, skip init scale if param is -1. + normalizations: [20., -1, -1, -1, -1, -1] + +# fluid.layers.multi_box_head, Generate prior boxes for SSD algorithm. +# Generate `prior_box` according to the inputs list and other parameters +# Each position of the input produce N prior boxes, N is determined by +# the count of min_sizes, max_sizes and aspect_ratios, The size of the box +# is in range(min_size, max_size) interval, which is generated in sequence +# according to the aspect_ratios. +MultiBoxHead: + # the base_size is used to get min_size and max_size according to min_ratio and max_ratio. + base_size: 300 + # the aspect ratios of generated prior boxes. The length of input and aspect_ratios must be equal. + aspect_ratios: [[2.], [2., 3.], [2., 3.], [2., 3.], [2.], [2.]] + # the min ratio of generated prior boxes. + min_ratio: 15 + # the max ratio of generated prior boxes. + max_ratio: 90 + # If len(inputs) <=2, min_sizes must be set up, and the length of min_sizes + # should equal to the length of inputs. Default: None. + min_sizes: [30.0, 60.0, 111.0, 162.0, 213.0, 264.0] + # If len(inputs) <=2, max_sizes must be set up, and the length of min_sizes + # should equal to the length of inputs. Default: None. + max_sizes: [60.0, 111.0, 162.0, 213.0, 264.0, 315.0] + # If step_w and step_h are the same, step_w and step_h can be replaced by steps. + steps: [8, 16, 32, 64, 100, 300] + # Prior boxes center offset. Default: 0.5 + offset: 0.5 + # Whether to flip aspect ratios. Default:False. + flip: true + # The kernel size of conv2d. Default: 1. + kernel_size: 3 + # The padding of conv2d. Default:0. + pad: 1 + +# Learning rate configuration +LearningRate: + # Base learning rate, 0.01 by default + base_lr: 0.001 + # Learning rate schedulers, PiecewiseDecay and LinearWarmup by default + schedulers: + # fluid.layers.piecewise_decay + # Values has higher priority and if values is null, learning rate is multipled by gamma at each stage + - !PiecewiseDecay + gamma: 0.1 + milestones: [280000, 360000] + # fluid.layers.linear_lr_warmup + # Start learning rate equals to base_lr * start_factor + - !LinearWarmup + start_factor: 0.3333333333333333 + steps: 500 + +# Optimizer module +OptimizerBuilder: + # fluid.optimizer, Neural network in essence is a Optimization problem . + # With forward computing and back propagation , Optimizer use back-propagation + # gradients to optimize parameters in a neural network. + optimizer: + # Momentum optimizer adds momentum on the basis of SGD , + # reducing noise problem in the process of random gradient descent. + momentum: 0.9 + type: Momentum + # fluid.regularizer + regularizer: + # implements the L2 Weight Decay Regularization + # Small values of L2 can help prevent over fitting the training data. + factor: 0.0005 + type: L2 + +# Data feed module for training +SSDTrainFeed: + # Batch size per device + batch_size: 16 + # list of batch transformations to use + batch_transforms: [] + # The data buffer size + bufsize: 10 + # Dataset module + dataset: + # Dataset directory + dataset_dir: dataset/coco + # Annotation file path + annotation: annotations/instances_train2017.json + # Directory where image files are stored + image_dir: train2017 + # Drop last batch if size is uneven, false by default + drop_last: true + # List of data fields needed + fields: [image, gt_box, gt_label] + # list of image dims + image_shape: [3, 300, 300] + # number of workers processes (or threads) + num_workers: 8 + # List of sample transformations to use + sample_transforms: + # Transform the image data to numpy format. + - !DecodeImage + # whether to convert BGR to RGB + to_rgb: true # default: true + # whether or not to mixup image and gt_bbbox/gt_score + with_mixup: false # default: false + # Transform the bounding box's coornidates to [0,1]. + - !NormalizeBox {} + # modify image brightness,contrast,saturation,hue,reordering channels and etc. + - !RandomDistort + # brightness_lower/ brightness_upper (float): the brightness + # between brightness_lower and brightness_upper + brightness_lower: 0.875 + brightness_upper: 1.125 + # brightness_prob (float): the probability of changing brightness + brightness_prob: 0.5 + # contrast_lower/ contrast_upper (float): the contrast between + # contrast_lower and contrast_lower + contrast_lower: 0.5 + contrast_upper: 1.5 + # contrast_prob (float): the probability of changing contrast + contrast_prob: 0.5 + # count (int): the kinds of doing distrot + count: 4 + # hue_lower/ hue_upper (float): the hue between hue_lower and hue_upper + hue_lower: -18 + hue_upper: 18 + # hue_prob (float): the probability of changing hue + hue_prob: 0.5 + # is_order (bool): whether determine the order of distortion + is_order: true + # saturation_lower/ saturation_upper (float): the saturation + # between saturation_lower and saturation_upper + saturation_lower: 0.5 + saturation_upper: 1.5 + # saturation_prob (float): the probability of changing saturation + saturation_prob: 0.5 + #Expand the image and modify bounding box. + # Operators: + # 1. Scale the image weight and height. + # 2. Construct new images with new height and width. + # 3. Fill the new image with the mean. + # 4. Put original imge into new image. + # 5. Rescale the bounding box. + # 6. Determine if the new bbox is satisfied in the new image. + - !ExpandImage + # max_ratio (float): the ratio of expanding + max_ratio: 4 + # mean (list): the pixel mean + mean: [104, 117, 123] + # prob (float): the probability of expanding image + prob: 0.5 + # Crop the image and modify bounding box. + # Operators: + # 1. Scale the image weight and height. + # 2. Crop the image according to a radom sample. + # 3. Rescale the bounding box. + # 4. Determine if the new bbox is satisfied in the new image. + - !CropImage + # avoid_no_bbox (bool): whether to to avoid the + # situation where the box does not appear. + avoid_no_bbox: false + # batch_sampler (list): Multiple sets of different parameters for cropping. + 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 (bool): whether all boxes must satisfy. + satisfy_all: false + # Rescale image to the specified target size, and capped at max_size if max_size != 0. + # If target_size is list, selected a scale randomly as the specified target size. + - !ResizeImage + # Resize method, cv2.INTER_LINEAR(1) by default + interp: 1 + # max_size (int): the max size of image + max_size: 0 + # target_size (int|list): the target size of image's short side, + # multi-scale training is adopted when type is list. + target_size: 300 + # use_cv2 (bool): use the cv2 interpolation method or use PIL interpolation method + use_cv2: false + # Filp the image and bounding box. + # Operators: + # 1. Flip the image numpy. + # 2. Transform the bboxes' x coordinates. (Must judge whether the coordinates are normalized!) + # 3. Transform the segmentations' x coordinates. (Must judge whether the coordinates are normalized!) + - !RandomFlipImage + # is_mask_flip (bool): whether flip the segmentation + is_mask_flip: false + # is_normalized (bool): whether the bbox scale to [0,1] + is_normalized: true + # prob (float): the probability of flipping image + prob: 0.5 + # Change the channel + - !Permute + # The format of image, [H, W, C]/[C, H, W], true by default + channel_first: true + # to_bgr (bool): confirm whether to convert RGB to BGR + to_bgr: true + # Normalize the image. + # Operators: + # 1.(optional) Scale the image to [0,1] + # 2. Each pixel minus mean and is divided by std + - !NormalizeImage + # The format of image, [H, W, C]/[C, H, W], true by default + is_channel_first: true + # Whether divide by 255, true by default + is_scale: false + # mean (list): the pixel mean + mean: [104, 117, 123] + # std (list): the pixel variance + std: [1, 1, 1] + # Number of samples, -1 represents all samples. -1 by default + samples: -1 + # If samples should be shuffled, true by default + shuffle: true + # If use multi-process, false by default + use_process: true + +# Data feed module for Eval +SSDEvalFeed: + # Batch size per device + batch_size: 32 + # list of batch transformations to use + batch_transforms: [] + # The data buffer size + bufsize: 10 + # Dataset module + dataset: + # Dataset directory + dataset_dir: dataset/coco + # Annotation file path + annotation: annotations/instances_val2017.json + # Directory where image files are stored + image_dir: val2017 + # Drop last batch if size is uneven, false by default + drop_last: true + # List of data fields needed + fields: [image, im_shape, im_id, gt_box, gt_label, is_difficult] + # list of image dims + image_shape: [3, 300, 300] + # number of workers processes (or threads) + num_workers: 8 + # List of sample transformations to use + sample_transforms: + # Transform the image data to numpy format. + - !DecodeImage + # whether to convert BGR to RGB + to_rgb: true # default: true + # whether or not to mixup image and gt_bbbox/gt_score + with_mixup: false # default: false + # Transform the bounding box's coornidates to [0,1]. + - !NormalizeBox {} + # Rescale image to the specified target size, and capped at max_size if max_size != 0. + # If target_size is list, selected a scale randomly as the specified target size. + - !ResizeImage + # Resize method, cv2.INTER_LINEAR(1) by default + interp: 1 + # max_size (int): the max size of image + max_size: 0 + # target_size (int|list): the target size of image's short side, + # multi-scale training is adopted when type is list. + target_size: 300 + # use_cv2 (bool): use the cv2 interpolation method or use PIL interpolation method + use_cv2: false + - !Permute + # The format of image, [H, W, C]/[C, H, W], true by default + channel_first: true + # to_bgr (bool): confirm whether to convert RGB to BGR + to_bgr: true + # Normalize the image. + # Operators: + # 1.(optional) Scale the image to [0,1] + # 2. Each pixel minus mean and is divided by std + - !NormalizeImage + # The format of image, [H, W, C]/[C, H, W], true by default + is_channel_first: true + # Whether divide by 255, true by default + is_scale: false + # mean (list): the pixel mean + mean: [104, 117, 123] + # std (list): the pixel variance + std: [1, 1, 1] + # Number of samples, -1 represents all samples. -1 by default + samples: -1 + # If samples should be shuffled, true by default + shuffle: false + # If use multi-process, false by default + use_process: false + +# Data feed module for test +SSDTestFeed: + # Batch size per device + batch_size: 1 + # list of batch transformations to use + batch_transforms: [] + # The data buffer size + bufsize: 10 + # Dataset module + dataset: + # Annotation file path + annotation: dataset/coco/annotations/instances_val2017.json + # Drop last batch if size is uneven, false by default + drop_last: false + # List of data fields needed + fields: [image, im_id] + # list of image dims + image_shape: [3, 300, 300] + # number of workers processes (or threads) + num_workers: 8 + # List of sample transformations to use + sample_transforms: + # Transform the image data to numpy format. + - !DecodeImage + # whether to convert BGR to RGB + to_rgb: true # default: true + # whether or not to mixup image and gt_bbbox/gt_score + with_mixup: false # default: false + # Rescale image to the specified target size, and capped at max_size if max_size != 0. + # If target_size is list, selected a scale randomly as the specified target size. + - !ResizeImage + # Resize method, cv2.INTER_LINEAR(1) by default + interp: 1 + # max_size (int): the max size of image + max_size: 0 + # target_size (int|list): the target size of image's short side, + # multi-scale training is adopted when type is list. + target_size: 300 + # use_cv2 (bool): use the cv2 interpolation method or use PIL interpolation method + use_cv2: false + - !Permute + # The format of image, [H, W, C]/[C, H, W], true by default + channel_first: true + # to_bgr (bool): confirm whether to convert RGB to BGR + to_bgr: true + # Normalize the image. + # Operators: + # 1.(optional) Scale the image to [0,1] + # 2. Each pixel minus mean and is divided by std + - !NormalizeImage + # The format of image, [H, W, C]/[C, H, W], true by default + is_channel_first: true + # Whether divide by 255, true by default + is_scale: false + # mean (list): the pixel mean + mean: [104, 117, 123] + # std (list): the pixel variance + std: [1, 1, 1] + # Number of samples, -1 represents all samples. -1 by default + samples: -1 + # If samples should be shuffled, true by default + shuffle: false + # If use multi-process, false by default + use_process: false diff --git a/docs/config_example/yolov3_darknet.yml b/docs/config_example/yolov3_darknet.yml new file mode 100644 index 0000000000000000000000000000000000000000..65f479b72bb2504f84bbed5fed20afd78bad0ea9 --- /dev/null +++ b/docs/config_example/yolov3_darknet.yml @@ -0,0 +1,323 @@ +# Architecture of detection, which is also the prefix of data feed module +architecture: YOLOv3 + +# Data feed module. +train_feed: YoloTrainFeed +eval_feed: YoloEvalFeed +test_feed: YoloTestFeed + +# Use GPU or CPU, true by default. +use_gpu: true + +# Maximum number of iteration. +# In YOLOv3 model, default iteration number is to train for 270 epoches. +max_iters: 500200 + +# Smooth the log output in specified iterations, 20 by default. +log_smooth_window: 20 + +# The number of iteration interval to display in training log. +log_iter: 20 + +# The directory to save models. +save_dir: output + +# Snapshot period. If training and test at same time, evaluate model at each snapshot_iter. 2000 by default. +snapshot_iter: 2000 + +# Evalution method, COCO and VOC are available. +metric: COCO + +# The path of oretrained wegiths. If url is provided, it will be downloaded and decompressed automatically. +pretrain_weights: https://paddle-imagenet-models-name.bj.bcebos.com/DarkNet53_pretrained.tar +# The path of final model for evaluation and test. +weights: output/yolov3_darknet/model_final + +# Number of classes, 80 for COCO and 20 for VOC. +num_classes: 80 + + +# YOLOv3 architecture, see https://arxiv.org/abs/1804.02767 +YOLOv3: + backbone: DarkNet + yolo_head: YOLOv3Head + +# Backbone module +DarkNet: + # Batch normalization type in training, sync_bn for synchronized batch normalization + norm_type: sync_bn + # L2 weight decay factor of batch normalization layer + norm_decay: 0. + # Darknet convolution layer number, only support 53 currently + depth: 53 + +# YOLOv3 head module +# Generate bbox output in evaluation and calculate loss in training +# fluid.layers.yolov3_loss / fluid.layers.yolo_box +YOLOv3Head: + # anchor mask of 3 yolo_loss/yolo_box layers, each yolo_loss/yolo_box layer has 3 anchors + anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]] + # 9 anchors for 3 yolo_loss/yolo_box layer, generated by perform kmeans on COCO gtboxes + anchors: [[10, 13], [16, 30], [33, 23], + [30, 61], [62, 45], [59, 119], + [116, 90], [156, 198], [373, 326]] + # L2 weight decay factor of batch normalization layer + norm_decay: 0. + # Ignore threshold for yolo_loss layer, 0.7 by default. + # Objectness loss will be ignored if a predcition bbox overlap a gtbox over ignore_thresh. + ignore_thresh: 0.7 + # Whether use label smooth in yolo_loss layer + # It is recommended to set as true when only num_classes is very big + label_smooth: true + # fluid.layers.multiclass_nms + # Non-max suppress for output prediction boxes, see multiclass_nms for following parameters. + # 1. Select detection bounding boxes with high scores larger than score_threshold. + # 2. Select detection bounding boxes with the largest nms_top_k scores. + # 3. Suppress detection bounding boxes which have high IoU overlap witch already selected boxes. + # 4. Keep the top keep_top_k detection bounding boxes as output. + nms: + # Which label is regard as backgroud and will be ignored, -1 for no backgroud label. + background_label: -1 + # Number of total bboxes to be kept per image after NMS step. + keep_top_k: 100 + # IoU threshold for NMS, bbox with IoU over nms_threshold will be suppressed. + nms_threshold: 0.45 + # Maximum number of detections to be kept according to the confidences after the filtering detections based on score_threshold. + nms_top_k: 1000 + # Whether detections are normalized. + normalized: false + # Threshold to filter out bounding boxes with low confidence score. + score_threshold: 0.01 + +# Learning rate configuration +LearningRate: + # Base learning rate for training, 1e-3 by default. + base_lr: 0.001 + # Learning rate schedulers, PiecewiseDecay and LinearWarmup by default + schedulers: + # fluid.layers.piecewise_decay + # each milestone stage decay gamma + - !PiecewiseDecay + gamma: 0.1 + milestones: + - 400000 + - 450000 + # fluid.layers.linear_lr_warmup + # Start learning rate equals to base_lr * start_factor + - !LinearWarmup + start_factor: 0. + steps: 4000 + +# Optimizer module +OptimizerBuilder: + # fluid.optimizer + optimizer: + momentum: 0.9 + type: Momentum + # fluid.regularizer + regularizer: + factor: 0.0005 + type: L2 + +# Data feed module for training +YoloTrainFeed: + # Batch size per device, 8 by default + batch_size: 8 + # Dataset module + dataset: + # Dataset directory. + dataset_dir: dataset/coco + # Annotation file path. + annotation: annotations/instances_train2017.json + # Directory where image files are stored. + image_dir: train2017 + # List of data fields needed. + fields: [image, gt_box, gt_label, gt_score] + # List of image dims + image_shape: [3, 608, 608] + # List of sample transformations to use. + sample_transforms: + # read image data and decode to numpy. + - !DecodeImage + to_rgb: true + # YOLOv3 use image mixup in training. + with_mixup: true + # Mixup two images in training, a trick to improve performance. + - !MixupImage + alpha: 1.5 # default: 1.5 + beta: 1.5 # default: 1.5 + # Normalize gtbox to range [0, 1] + - !NormalizeBox {} + # Random color distort: brightness, contrast, hue, saturation. + - !RandomDistort + brightness_lower: 0.5 + brightness_prob: 0.5 + brightness_upper: 1.5 + contrast_lower: 0.5 + contrast_prob: 0.5 + contrast_upper: 1.5 + count: 4 + hue_lower: -18 + hue_prob: 0.5 + hue_upper: 18 + is_order: false + saturation_lower: 0.5 + saturation_prob: 0.5 + saturation_upper: 1.5 + # Random Expand the image and modify bounding box. + # Operators: + # 1. Scale the image weight and height. + # 2. Construct new images with new height and width. + # 3. Fill the new image with the mean. + # 4. Put original imge into new image. + # 5. Rescale the bounding box. + # 6. Determine if the new bbox is satisfied in the new image. + - !ExpandImage + # max expand ratio, default 4.0. + max_ratio: 4.0 + mean: [123.675, 116.28, 103.53] + prob: 0.5 + # Random Crop the image and modify bounding box. + # Operators: + # 1. Scale the image weight and height. + # 2. Crop the image according to a radom sample. + # 3. Rescale the bounding box. + # 4. Determine if the new bbox is satisfied in the new image. + - !CropImage + # Recrop image if there are no bbox in output cropped image. + 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, 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]] + # Whether should all bbox satisfy IoU constrains. + satisfy_all: false + # Interpolate image to target_size with random interpolate method: + # cv2.INTER_NEAREST, + # cv2.INTER_LINEAR, + # cv2.INTER_AREA, + # cv2.INTER_CUBIC, + # cv2.INTER_LANCZOS4, + - !RandomInterpImage + max_size: 0 + target_size: 608 + # Filp the image and bounding box. + # Operators: + # 1. Flip the image numpy. + # 2. Transform the bboxes' x coordinates. (Must judge whether the coordinates are normalized!) + # 3. Transform the segmentations' x coordinates. (Must judge whether the coordinates are normalized!) + - !RandomFlipImage + is_mask_flip: false + is_normalized: true + prob: 0.5 + # Normalize the image. + # Operators: + # 1.(optional) Scale the image to [0,1] + # 2. Each pixel minus mean and is divided by std + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485, 0.456, 0.406] + std: [0.229, 0.224, 0.225] + # Change data layout to [C, H, W]. + - !Permute + channel_first: true + to_bgr: false + # List of batch transformations to use. + batch_transforms: + # Random reshape images in each mini-batch to different shapes. + - !RandomShape + sizes: [320, 352, 384, 416, 448, 480, 512, 544, 576, 608] + # YOLOv3 read gtbox into zero padded tensor with max box number as 50. + num_max_boxes: 50 + # YOLOv3 read gtlabel without regarding backgroud as label 0. + with_background: false + # Number of samples, -1 represents all samples. -1 by default. + samples: -1 + # Whether samples should be shuffled, true by default. + shuffle: true + # Whether drop last images which less than a batch. + drop_last: true + # Whether use multi-process reader in training. + use_process: true + # Use multi-process reader number. + num_workers: 8 + # Buffer size for reader. + bufsize: 128 + # Mixup image epoch number. + mixup_epoch: 250 + +# Data feed module for evaluation +YoloEvalFeed: + batch_size: 8 + dataset: + dataset_dir: dataset/coco + annotation: annotations/instances_val2017.json + image_dir: val2017 + batch_transforms: [] + fields: [image, im_size, im_id, gt_box, gt_label, is_difficult] + image_shape: [3, 608, 608] + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + # Rescale image to the specified target size, and capped at max_size if max_size != 0. + # If target_size is list, selected a scale randomly as the specified target size. + - !ResizeImage + interp: 2 # 2 for cv2.INTER_CUBIC + max_size: 0 + target_size: 608 + use_cv2: true + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485, 0.456, 0.406] + std: [0.229, 0.224, 0.225] + - !Permute + channel_first: true + to_bgr: false + num_max_boxes: 50 + samples: -1 + shuffle: false + drop_last: false + # Use multi-thread reader in evaluation mode. + use_process: false + # Thread number for multi-thread reader. + num_workers: 8 + with_background: false + +# Data feed module for test +YoloTestFeed: + batch_size: 1 + dataset: + annotation: dataset/coco/annotations/instances_val2017.json + batch_transforms: [] + fields: [image, im_size, im_id] + sample_transforms: + - !DecodeImage + to_rgb: true + with_mixup: false + - !ResizeImage + interp: 2 + max_size: 0 + target_size: 608 + use_cv2: true + - !NormalizeImage + is_channel_first: false + is_scale: true + mean: [0.485, 0.456, 0.406] + std: [0.229, 0.224, 0.225] + - !Permute + channel_first: true + to_bgr: false + num_max_boxes: 50 + samples: -1 + shuffle: false + drop_last: false + # Use multi-thread reader in test mode. + use_process: false + num_workers: 8 + with_background: false diff --git a/docs/images/bench_ssd_yolo_infer.png b/docs/images/bench_ssd_yolo_infer.png new file mode 100644 index 0000000000000000000000000000000000000000..f81600b14cbe9af4b12f86c574ce2dffa937774f Binary files /dev/null and b/docs/images/bench_ssd_yolo_infer.png differ diff --git a/inference/CMakeLists.txt b/inference/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ed610da047316d0b08d73d51e0223a06180b4026 --- /dev/null +++ b/inference/CMakeLists.txt @@ -0,0 +1,272 @@ +cmake_minimum_required(VERSION 3.0) +project(cpp_inference_demo CXX C) +message("cmake module path: ${CMAKE_MODULE_PATH}") +message("cmake root path: ${CMAKE_ROOT}") +option(WITH_MKL "Compile demo with MKL/OpenBlas support,defaultuseMKL." ON) +option(WITH_GPU "Compile demo with GPU/CPU, default use CPU." ON) +option(WITH_STATIC_LIB "Compile demo with static/shared library, default use static." ON) +option(USE_TENSORRT "Compile demo with TensorRT." OFF) + +SET(PADDLE_DIR "" CACHE PATH "Location of libraries") +SET(OPENCV_DIR "" CACHE PATH "Location of libraries") +SET(CUDA_LIB "" CACHE PATH "Location of libraries") + + +include(external-cmake/yaml-cpp.cmake) + +macro(safe_set_static_flag) + foreach(flag_var + CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + if(${flag_var} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") + endif(${flag_var} MATCHES "/MD") + endforeach(flag_var) +endmacro() + +if (WITH_MKL) + ADD_DEFINITIONS(-DUSE_MKL) +endif() + +if (NOT DEFINED PADDLE_DIR OR ${PADDLE_DIR} STREQUAL "") + message(FATAL_ERROR "please set PADDLE_DIR with -DPADDLE_DIR=/path/paddle_influence_dir") +endif() + +if (NOT DEFINED OPENCV_DIR OR ${OPENCV_DIR} STREQUAL "") + message(FATAL_ERROR "please set OPENCV_DIR with -DOPENCV_DIR=/path/opencv") +endif() + +include_directories("${CMAKE_SOURCE_DIR}/") +include_directories("${CMAKE_CURRENT_BINARY_DIR}/ext/yaml-cpp/src/ext-yaml-cpp/include") +include_directories("${PADDLE_DIR}/") +include_directories("${PADDLE_DIR}/third_party/install/protobuf/include") +include_directories("${PADDLE_DIR}/third_party/install/glog/include") +include_directories("${PADDLE_DIR}/third_party/install/gflags/include") +include_directories("${PADDLE_DIR}/third_party/install/xxhash/include") +if (EXISTS "${PADDLE_DIR}/third_party/install/snappy/include") + include_directories("${PADDLE_DIR}/third_party/install/snappy/include") +endif() +if(EXISTS "${PADDLE_DIR}/third_party/install/snappystream/include") + include_directories("${PADDLE_DIR}/third_party/install/snappystream/include") +endif() +include_directories("${PADDLE_DIR}/third_party/install/zlib/include") +include_directories("${PADDLE_DIR}/third_party/boost") +include_directories("${PADDLE_DIR}/third_party/eigen3") + +if (EXISTS "${PADDLE_DIR}/third_party/install/snappy/lib") + link_directories("${PADDLE_DIR}/third_party/install/snappy/lib") +endif() +if(EXISTS "${PADDLE_DIR}/third_party/install/snappystream/lib") + link_directories("${PADDLE_DIR}/third_party/install/snappystream/lib") +endif() + +link_directories("${PADDLE_DIR}/third_party/install/zlib/lib") +link_directories("${PADDLE_DIR}/third_party/install/protobuf/lib") +link_directories("${PADDLE_DIR}/third_party/install/glog/lib") +link_directories("${PADDLE_DIR}/third_party/install/gflags/lib") +link_directories("${PADDLE_DIR}/third_party/install/xxhash/lib") +link_directories("${PADDLE_DIR}/paddle/lib/") +link_directories("${CMAKE_CURRENT_BINARY_DIR}/ext/yaml-cpp/lib") +link_directories("${CMAKE_CURRENT_BINARY_DIR}") +if (WIN32) + include_directories("${PADDLE_DIR}/paddle/fluid/inference") + link_directories("${PADDLE_DIR}/paddle/fluid/inference") + include_directories("${OPENCV_DIR}/build/include") + include_directories("${OPENCV_DIR}/opencv/build/include") + link_directories("${OPENCV_DIR}/build/x64/vc14/lib") +else () + include_directories("${PADDLE_DIR}/paddle/include") + link_directories("${PADDLE_DIR}/paddle/lib") + include_directories("${OPENCV_DIR}/include") + link_directories("${OPENCV_DIR}/lib") +endif () + +if (WIN32) + add_definitions("/DGOOGLE_GLOG_DLL_DECL=") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /bigobj /MTd") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /bigobj /MT") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /bigobj /MTd") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /bigobj /MT") + if (WITH_STATIC_LIB) + safe_set_static_flag() + add_definitions(-DSTATIC_LIB) + endif() +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -o2 -std=c++11") + set(CMAKE_STATIC_LIBRARY_PREFIX "") +endif() + +# TODO let users define cuda lib path +if (WITH_GPU) + if (NOT DEFINED CUDA_LIB OR ${CUDA_LIB} STREQUAL "") + message(FATAL_ERROR "please set CUDA_LIB with -DCUDA_LIB=/path/cuda-8.0/lib64") + endif() + if (NOT WIN32) + if (NOT DEFINED CUDNN_LIB) + message(FATAL_ERROR "please set CUDNN_LIB with -DCUDNN_LIB=/path/cudnn_v7.4/cuda/lib64") + endif() + endif(NOT WIN32) +endif() + + +if (NOT WIN32) + if (USE_TENSORRT AND WITH_GPU) + include_directories("${PADDLE_DIR}/third_party/install/tensorrt/include") + link_directories("${PADDLE_DIR}/third_party/install/tensorrt/lib") + endif() +endif(NOT WIN32) + +if (NOT WIN32) + set(NGRAPH_PATH "${PADDLE_DIR}/third_party/install/ngraph") + if(EXISTS ${NGRAPH_PATH}) + include(GNUInstallDirs) + include_directories("${NGRAPH_PATH}/include") + link_directories("${NGRAPH_PATH}/${CMAKE_INSTALL_LIBDIR}") + set(NGRAPH_LIB ${NGRAPH_PATH}/${CMAKE_INSTALL_LIBDIR}/libngraph${CMAKE_SHARED_LIBRARY_SUFFIX}) + endif() +endif() + +if(WITH_MKL) + include_directories("${PADDLE_DIR}/third_party/install/mklml/include") + if (WIN32) + set(MATH_LIB ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.lib + ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.lib) + else () + set(MATH_LIB ${PADDLE_DIR}/third_party/install/mklml/lib/libmklml_intel${CMAKE_SHARED_LIBRARY_SUFFIX} + ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5${CMAKE_SHARED_LIBRARY_SUFFIX}) + endif () + set(MKLDNN_PATH "${PADDLE_DIR}/third_party/install/mkldnn") + if(EXISTS ${MKLDNN_PATH}) + include_directories("${MKLDNN_PATH}/include") + if (WIN32) + set(MKLDNN_LIB ${MKLDNN_PATH}/lib/mkldnn.lib) + else () + set(MKLDNN_LIB ${MKLDNN_PATH}/lib/libmkldnn.so.0) + endif () + endif() +else() + set(MATH_LIB ${PADDLE_DIR}/third_party/install/openblas/lib/libopenblas${CMAKE_STATIC_LIBRARY_SUFFIX}) +endif() + +if(WITH_STATIC_LIB) + if (WIN32) + set(DEPS + ${PADDLE_DIR}/paddle/fluid/inference/libpaddle_fluid${CMAKE_STATIC_LIBRARY_SUFFIX}) + else () + set(DEPS + ${PADDLE_DIR}/paddle/lib/libpaddle_fluid${CMAKE_STATIC_LIBRARY_SUFFIX}) + endif() +else() + if (WIN32) + set(DEPS + ${PADDLE_DIR}/paddle/fluid/inference/libpaddle_fluid${CMAKE_STATIC_LIBRARY_SUFFIX}) + else () + set(DEPS + ${PADDLE_DIR}/paddle/lib/libpaddle_fluid${CMAKE_SHARED_LIBRARY_SUFFIX}) + endif() +endif() + +if (NOT WIN32) + set(EXTERNAL_LIB "-lrt -ldl -lpthread") + set(DEPS ${DEPS} + ${MATH_LIB} ${MKLDNN_LIB} + glog gflags protobuf yaml-cpp z xxhash + ${EXTERNAL_LIB}) + if(EXISTS "${PADDLE_DIR}/third_party/install/snappystream/lib") + set(DEPS ${DEPS} snappystream) + endif() + if (EXISTS "${PADDLE_DIR}/third_party/install/snappy/lib") + set(DEPS ${DEPS} snappy) + endif() +else() + set(DEPS ${DEPS} + ${MATH_LIB} ${MKLDNN_LIB} + opencv_world346 glog libyaml-cppmt gflags_static libprotobuf zlibstatic xxhash ${EXTERNAL_LIB}) + set(DEPS ${DEPS} libcmt shlwapi) + if (EXISTS "${PADDLE_DIR}/third_party/install/snappy/lib") + set(DEPS ${DEPS} snappy) + endif() + if(EXISTS "${PADDLE_DIR}/third_party/install/snappystream/lib") + set(DEPS ${DEPS} snappystream) + endif() +endif(NOT WIN32) + +if(WITH_GPU) + if(NOT WIN32) + if (USE_TENSORRT) + set(DEPS ${DEPS} ${PADDLE_DIR}/third_party/install/tensorrt/lib/libnvinfer${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${PADDLE_DIR}/third_party/install/tensorrt/lib/libnvinfer_plugin${CMAKE_STATIC_LIBRARY_SUFFIX}) + endif() + set(DEPS ${DEPS} ${CUDA_LIB}/libcudart${CMAKE_SHARED_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${CUDNN_LIB}/libcudnn${CMAKE_SHARED_LIBRARY_SUFFIX}) + else() + set(DEPS ${DEPS} ${CUDA_LIB}/cudart${CMAKE_STATIC_LIBRARY_SUFFIX} ) + set(DEPS ${DEPS} ${CUDA_LIB}/cublas${CMAKE_STATIC_LIBRARY_SUFFIX} ) + set(DEPS ${DEPS} ${CUDA_LIB}/cudnn${CMAKE_STATIC_LIBRARY_SUFFIX}) + endif() +endif() + +if (NOT WIN32) + set(OPENCV_LIB_DIR ${OPENCV_DIR}/lib) + if(EXISTS "${OPENCV_LIB_DIR}") + message("OPENCV_LIB:" ${OPENCV_LIB_DIR}) + else() + set(OPENCV_LIB_DIR ${OPENCV_DIR}/lib64) + message("OPENCV_LIB:" ${OPENCV_LIB_DIR}) + endif() + + set(OPENCV_3RD_LIB_DIR ${OPENCV_DIR}/share/OpenCV/3rdparty/lib) + if(EXISTS "${OPENCV_3RD_LIB_DIR}") + message("OPENCV_3RD_LIB_DIR:" ${OPENCV_3RD_LIB_DIR}) + else() + set(OPENCV_3RD_LIB_DIR ${OPENCV_DIR}/share/OpenCV/3rdparty/lib64) + message("OPENCV_3RD_LIB_DIR:" ${OPENCV_3RD_LIB_DIR}) + endif() + + set(DEPS ${DEPS} ${OPENCV_LIB_DIR}/libopencv_imgcodecs${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${OPENCV_LIB_DIR}/libopencv_imgproc${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${OPENCV_LIB_DIR}/libopencv_core${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${OPENCV_LIB_DIR}/libopencv_highgui${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${OPENCV_3RD_LIB_DIR}/libIlmImf${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${OPENCV_3RD_LIB_DIR}/liblibjasper${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${OPENCV_3RD_LIB_DIR}/liblibpng${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${OPENCV_3RD_LIB_DIR}/liblibtiff${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${OPENCV_3RD_LIB_DIR}/libittnotify${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${OPENCV_3RD_LIB_DIR}/liblibjpeg-turbo${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${OPENCV_3RD_LIB_DIR}/liblibwebp${CMAKE_STATIC_LIBRARY_SUFFIX}) + set(DEPS ${DEPS} ${OPENCV_3RD_LIB_DIR}/libzlib${CMAKE_STATIC_LIBRARY_SUFFIX}) + if(EXISTS "${OPENCV_3RD_LIB_DIR}/libippiw${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(DEPS ${DEPS} ${OPENCV_3RD_LIB_DIR}/libippiw${CMAKE_STATIC_LIBRARY_SUFFIX}) + endif() + if(EXISTS "${OPENCV_3RD_LIB_DIR}/libippicv${CMAKE_STATIC_LIBRARY_SUFFIX}") + set(DEPS ${DEPS} ${OPENCV_3RD_LIB_DIR}/libippicv${CMAKE_STATIC_LIBRARY_SUFFIX}) + endif() +endif() +# message(${CMAKE_CXX_FLAGS}) +# set(CMAKE_CXX_FLAGS "-g ${CMAKE_CXX_FLAGS}") + +SET(PADDLESEG_INFERENCE_SRCS preprocessor/preprocessor.cpp + preprocessor/preprocessor_detection.cpp predictor/detection_predictor.cpp + utils/detection_result.pb.cc) + +ADD_LIBRARY(libpaddleseg_inference STATIC ${PADDLESEG_INFERENCE_SRCS}) +target_link_libraries(libpaddleseg_inference ${DEPS}) + +add_executable(detection_demo detection_demo.cpp) + +ADD_DEPENDENCIES(libpaddleseg_inference ext-yaml-cpp) +ADD_DEPENDENCIES(detection_demo ext-yaml-cpp libpaddleseg_inference) +target_link_libraries(detection_demo ${DEPS} libpaddleseg_inference) + +if (WIN32) + add_custom_command(TARGET detection_demo POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./mklml.dll + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./libiomp5md.dll + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mkldnn/lib/mkldnn.dll ./mkldnn.dll + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/mklml.dll ./release/mklml.dll + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mklml/lib/libiomp5md.dll ./release/libiomp5md.dll + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PADDLE_DIR}/third_party/install/mkldnn/lib/mkldnn.dll ./mkldnn.dll + ) +endif() + +execute_process(COMMAND cp -r ${CMAKE_SOURCE_DIR}/images ${CMAKE_SOURCE_DIR}/conf ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/inference/LICENSE b/inference/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64 --- /dev/null +++ b/inference/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/inference/README.md b/inference/README.md new file mode 100644 index 0000000000000000000000000000000000000000..302b5fb3818df8c1ca871095ff368129ce1292fd --- /dev/null +++ b/inference/README.md @@ -0,0 +1,171 @@ +# PaddleDetection C++预测部署方案 + +## 本文档结构 + +[1.说明](#1说明) + +[2.主要目录和文件](#2主要目录和文件) + +[3.编译](#3编译) + +[4.预测并可视化结果](#4预测并可视化结果) + + + + +## 1.说明 + +本目录提供一个跨平台的图像检测模型的C++预测部署方案,用户通过一定的配置,加上少量的代码,即可把模型集成到自己的服务中,完成相应的图像检测任务。 + +主要设计的目标包括以下四点: +- 跨平台,支持在 Windows 和 Linux 完成编译、开发和部署 +- 可扩展性,支持用户针对新模型开发自己特殊的数据预处理等逻辑 +- 高性能,除了`PaddlePaddle`自身带来的性能优势,我们还针对图像检测的特点对关键步骤进行了性能优化 +- 支持多种常见的图像检测模型,如YOLOv3, Faster-RCNN, Faster-RCNN+FPN,用户通过少量配置即可加载模型完成常见检测任务 + +## 2.主要目录和文件 + +```bash +deploy +├── detection_demo.cpp # 完成图像检测预测任务C++代码 +│ +├── conf +│ ├── detection_rcnn.yaml #示例faster rcnn 目标检测配置 +│ └── detection_rcnn_fpn.yaml #示例faster rcnn + fpn目标检测配置 +├── images +│ └── detection_rcnn # 示例faster rcnn + fpn目标检测测试图片目录 +├── tools +│ └── vis.py # 示例图像检测结果可视化脚本 +├── docs +│ ├── linux_build.md # Linux 编译指南 +│ ├── windows_vs2015_build.md # windows VS2015编译指南 +│ └── windows_vs2019_build.md # Windows VS2019编译指南 +│ +├── utils # 一些基础公共函数 +│ +├── preprocess # 数据预处理相关代码 +│ +├── predictor # 模型加载和预测相关代码 +│ +├── CMakeList.txt # cmake编译入口文件 +│ +└── external-cmake # 依赖的外部项目cmake(目前仅有yaml-cpp) + +``` + +## 3.编译 +支持在`Windows`和`Linux`平台编译和使用: +- [Linux 编译指南](./docs/linux_build.md) +- [Windows 使用 Visual Studio 2019 Community 编译指南](./docs/windows_vs2019_build.md) +- [Windows 使用 Visual Studio 2015 编译指南](./docs/windows_vs2015_build.md) + +`Windows`上推荐使用最新的`Visual Studio 2019 Community`直接编译`CMake`项目。 + +## 4.预测并可视化结果 + +完成编译后,便生成了需要的可执行文件和链接库。这里以我们基于`faster rcnn`检测模型为例,介绍部署图像检测模型的通用流程。 + +### 1. 下载模型文件 +我们提供faster rcnn,faster rcnn+fpn模型用于预测coco17数据集,可在以下链接下载:[faster rcnn示例模型下载地址](https://paddleseg.bj.bcebos.com/inference/faster_rcnn_pp50.zip), + [faster rcnn + fpn示例模型下载地址](https://paddleseg.bj.bcebos.com/inference/faster_rcnn_pp50_fpn.zip)。 + +下载并解压,解压后目录结构如下: +``` +faster_rcnn_pp50/ +├── __model__ # 模型文件 +│ +└── __params__ # 参数文件 +``` +解压后把上述目录拷贝到合适的路径: + +**假设**`Windows`系统上,我们模型和参数文件所在路径为`D:\projects\models\faster_rcnn_pp50`。 + +**假设**`Linux`上对应的路径则为`/root/projects/models/faster_rcnn_pp50/`。 + + +### 2. 修改配置 + +`inference`源代码(即本目录)的`conf`目录下提供了示例基于faster rcnn的配置文件`detection_rcnn.yaml`, 相关的字段含义和说明如下: + +```yaml +DEPLOY: + # 是否使用GPU预测 + USE_GPU: 1 + # 模型和参数文件所在目录路径 + MODEL_PATH: "/root/projects/models/faster_rcnn_pp50" + # 模型文件名 + MODEL_FILENAME: "__model__" + # 参数文件名 + PARAMS_FILENAME: "__params__" + # 预测图片的标准输入,尺寸不一致会resize + EVAL_CROP_SIZE: (608, 608) + # resize方式,支持 UNPADDING和RANGE_SCALING + RESIZE_TYPE: "RANGE_SCALING" + # 短边对齐的长度,仅在RANGE_SCALING下有效 + TARGET_SHORT_SIZE : 800 + # 均值 + MEAN: [0.4647, 0.4647, 0.4647] + # 方差 + STD: [0.0834, 0.0834, 0.0834] + # 图片类型, rgb或者rgba + IMAGE_TYPE: "rgb" + # 像素分类数 + NUM_CLASSES: 1 + # 通道数 + CHANNELS : 3 + # 预处理器, 目前提供图像检测的通用处理类DetectionPreProcessor + PRE_PROCESSOR: "DetectionPreProcessor" + # 预测模式,支持 NATIVE 和 ANALYSIS + PREDICTOR_MODE: "ANALYSIS" + # 每次预测的 batch_size + BATCH_SIZE : 3 + # 长边伸缩的最大长度,-1代表无限制。 + RESIZE_MAX_SIZE: 1333 + # 输入的tensor数量。 + FEEDS_SIZE: 3 + +``` +修改字段`MODEL_PATH`的值为你在**上一步**下载并解压的模型文件所放置的目录即可。更多配置文件字段介绍,请参考文档[预测部署方案配置文件说明](./docs/configuration.md)。 + +### 3. 执行预测 + +在终端中切换到生成的可执行文件所在目录为当前目录(Windows系统为`cmd`)。 + +`Linux` 系统中执行以下命令: +```shell +./detection_demo --conf=conf/detection_rcnn.yaml --input_dir=images/detection_rcnn +``` +`Windows` 中执行以下命令: +```shell +.\detection_demo.exe --conf=conf\detection_rcnn.yaml --input_dir=images\detection_rcnn\ +``` + + +预测使用的两个命令参数说明如下: + +| 参数 | 含义 | +|-------|----------| +| conf | 模型配置的Yaml文件路径 | +| input_dir | 需要预测的图片目录 | + +· +配置文件说明请参考上一步,样例程序会扫描input_dir目录下的所有图片,并为每一张图片生成对应的预测结果,输出到屏幕,并在`X`同一目录下保存到`X.pb文件`(X为对应图片的文件名)。可使用工具脚本vis.py将检测结果可视化。 + +**检测结果可视化** + +运行可视化脚本时,只需输入命令行参数图片路径、检测结果pb文件路径、目标框阈值以及类别-标签映射文件路径即可得到可视化的图片`X.png` (tools目录下提供coco17的类别标签映射文件coco17.json)。 + +```bash +python vis.py --img_path=../build/images/detection_rcnn/000000087038.jpg --img_result_path=../build/images/detection_rcnn/000000087038.jpg.pb --threshold=0.1 --c2l_path=coco17.json +``` + +检测结果(每个图片的结果用空行隔开) + +```原图:``` + +![原图](./demo_images/000000087038.jpg) + +```检测结果图:``` + +![检测结果](./demo_images/000000087038.jpg.png) + diff --git a/inference/conf/detection_rcnn.yaml b/inference/conf/detection_rcnn.yaml new file mode 100644 index 0000000000000000000000000000000000000000..50c23fbb3e53ff159844e65da4ed194e169cffb6 --- /dev/null +++ b/inference/conf/detection_rcnn.yaml @@ -0,0 +1,18 @@ +DEPLOY: + USE_GPU: 1 + MODEL_PATH: "/root/projects/models/faster_rcnn_pp50" + MODEL_FILENAME: "__model__" + PARAMS_FILENAME: "__params__" + EVAL_CROP_SIZE: (608, 608) + RESIZE_TYPE: "RANGE_SCALING" + TARGET_SHORT_SIZE : 800 + MEAN: [0.485, 0.456, 0.406] + STD: [0.229, 0.224, 0.225] + IMAGE_TYPE: "rgb" + NUM_CLASSES: 1 + CHANNELS : 3 + PRE_PROCESSOR: "DetectionPreProcessor" + PREDICTOR_MODE: "ANALYSIS" + BATCH_SIZE : 3 + RESIZE_MAX_SIZE: 1333 + FEEDS_SIZE: 3 diff --git a/inference/conf/detection_rcnn_fpn.yaml b/inference/conf/detection_rcnn_fpn.yaml new file mode 100644 index 0000000000000000000000000000000000000000..9d6635ef8c2b29fb0ca9318d1ec08f1f7be037f7 --- /dev/null +++ b/inference/conf/detection_rcnn_fpn.yaml @@ -0,0 +1,19 @@ +DEPLOY: + USE_GPU: 1 + MODEL_PATH: "/root/projects/models/faster_rcnn_pp50_fpn" + MODEL_FILENAME: "__model__" + PARAMS_FILENAME: "__params__" + EVAL_CROP_SIZE: (608, 608) + RESIZE_TYPE: "RANGE_SCALING" + TARGET_SHORT_SIZE : 800 + MEAN: [0.485, 0.456, 0.406] + STD: [0.229, 0.224, 0.225] + IMAGE_TYPE: "rgb" + NUM_CLASSES: 1 + CHANNELS : 3 + PRE_PROCESSOR: "DetectionPreProcessor" + PREDICTOR_MODE: "ANALYSIS" + BATCH_SIZE : 1 + RESIZE_MAX_SIZE: 1333 + FEEDS_SIZE: 3 + COARSEST_STRIDE: 32 diff --git a/inference/demo_images/000000087038.jpg b/inference/demo_images/000000087038.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9f77f5d5f057b6f92dc096da704ecb8dee99bdf5 Binary files /dev/null and b/inference/demo_images/000000087038.jpg differ diff --git a/inference/demo_images/000000087038.jpg.png b/inference/demo_images/000000087038.jpg.png new file mode 100644 index 0000000000000000000000000000000000000000..aa2c63d1c3dd1ca08d517239842ce5bd40310d01 Binary files /dev/null and b/inference/demo_images/000000087038.jpg.png differ diff --git a/inference/detection_demo.cpp b/inference/detection_demo.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7e711ed6970358c528a3198bb6168a871d83d380 --- /dev/null +++ b/inference/detection_demo.cpp @@ -0,0 +1,42 @@ +// 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. + +#include +#include +#include + +DEFINE_string(conf, "", "Configuration File Path"); +DEFINE_string(input_dir, "", "Directory of Input Images"); + +int main(int argc, char** argv) { + // 0. parse args + google::ParseCommandLineFlags(&argc, &argv, true); + if (FLAGS_conf.empty() || FLAGS_input_dir.empty()) { + std::cout << "Usage: ./predictor --conf=/config/path/to/your/model --input_dir=/directory/of/your/input/images"; + return -1; + } + // 1. create a predictor and init it with conf + PaddleSolution::DetectionPredictor predictor; + if (predictor.init(FLAGS_conf) != 0) { + LOG(FATAL) << "Fail to init predictor"; + return -1; + } + + // 2. get all the images with extension '.jpeg' at input_dir + auto imgs = PaddleSolution::utils::get_directory_images(FLAGS_input_dir, ".jpeg|.jpg|.JPEG|.JPG|.bmp|.BMP|.png|.PNG"); + + // 3. predict + predictor.predict(imgs); + return 0; +} diff --git a/inference/docs/configuration.md b/inference/docs/configuration.md new file mode 100644 index 0000000000000000000000000000000000000000..cb2f761f792009c1accb52048d6e4f2cdcb6ad29 --- /dev/null +++ b/inference/docs/configuration.md @@ -0,0 +1,75 @@ +# 预测部署方案配置文件说明 +## 基本概念 +预测部署方案的配置文件旨在给用户提供一个预测部署方案定制化接口。用户仅需理解该配置文件相关字段的含义,无需编写任何代码,即可定制化预测部署方案。为了更好地表达每个字段的含义,首先介绍配置文件中字段的类型。 + +### 字段类型 +- **required**: 表明该字段必须显式定义,否则无法正常启动预测部署程序。 +- **optional**: 表明该字段可忽略不写,预测部署系统会提供默认值,相关默认值将在下文介绍。 + +### 字段值类型 +- **int**:表明该字段必须赋予整型类型的值。 +- **string**:表明该字段必须赋予字符串类型的值。 +- **list**:表明该字段必须赋予列表的值。 +- **tuple**: 表明该字段必须赋予双元素元组的值。 + +## 字段介绍 + +```yaml +# 预测部署时所有配置字段需在DEPLOY字段下 +DEPLOY: + # 类型:required int + # 含义:是否使用GPU预测。 0:不使用 1:使用 + USE_GPU: 1 + # 类型:required string + # 含义:模型和参数文件所在目录 + MODEL_PATH: "/path/to/model_directory" + # 类型:required string + # 含义:模型文件名 + MODEL_FILENAME: "__model__" + # 类型:required string + # 含义:参数文件名 + PARAMS_FILENAME: "__params__" + # 类型:optional string + # 含义:图像resize的类型。支持 UNPADDING 和 RANGE_SCALING模式。默认是UNPADDING模式。 + RESIZE_TYPE: "UNPADDING" + # 类型:required tuple + # 含义:当使用UNPADDING模式时,会将图像直接resize到该尺寸。 + EVAL_CROP_SIZE: (513, 513) + # 类型:optional int + # 含义:当使用RANGE_SCALING模式时,图像短边需要对齐该字段的值,长边会同比例 + # 的缩放,从而在保持图像长宽比例不变的情况下resize到新的尺寸。默认值为0。 + TARGET_SHORT_SIZE: 800 + # 类型:optional int + # 含义: 当使用RANGE_SCALING模式时,长边不能缩放到比该字段的值大。默认值为0。 + RESIZE_MAX_SIZE: 1333 + # 类型:required list + # 含义:图像进行归一化预处理时的均值 + MEAN: [104.008, 116.669, 122.675] + # 类型:required list + # 含义:图像进行归一化预处理时的方差 + STD: [1.0, 1.0, 1.0] + # 类型:string + # 含义:图片类型, rgb 或者 rgba + IMAGE_TYPE: "rgb" + # 类型:required int + # 含义:图像分类类型数 + NUM_CLASSES: 2 + # 类型:required int + # 含义:图片通道数 + CHANNELS : 3 + # 类型:required string + # 含义:预处理方式,目前提供图像检测的通用预处理类DetectionPreProcessor. + PRE_PROCESSOR: "DetectionPreProcessor" + # 类型:required string + # 含义:预测模式,支持 NATIVE 和 ANALYSIS + PREDICTOR_MODE: "ANALYSIS" + # 类型:required int + # 含义:每次预测的 batch_size + BATCH_SIZE : 3 + # 类型:optional int + # 含义: 输入张量的个数。大部分模型不需要设置。 默认值为1. + FEEDS_SIZE: 2 + # 类型: optional int + # 含义: 将图像的边变为该字段的值的整数倍。默认值为1。 + COARSEST_STRIDE: 32 +``` \ No newline at end of file diff --git a/inference/docs/linux_build.md b/inference/docs/linux_build.md new file mode 100644 index 0000000000000000000000000000000000000000..2ad9e46383123efee47b941f97c8e7690c7b95d6 --- /dev/null +++ b/inference/docs/linux_build.md @@ -0,0 +1,84 @@ +# Linux平台 编译指南 + +## 说明 +本文档在 `Linux`平台使用`GCC 4.8.5` 和 `GCC 4.9.4`测试过,如果需要使用更高G++版本编译使用,则需要重新编译Paddle预测库,请参考: [从源码编译Paddle预测库](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/advanced_usage/deploy/inference/build_and_install_lib_cn.html#id15)。 + +## 前置条件 +* G++ 4.8.2 ~ 4.9.4 +* CUDA 8.0/ CUDA 9.0 +* CMake 3.0+ + +请确保系统已经安装好上述基本软件,**下面所有示例以工作目录为 `/root/projects/`演示**。 + +### Step1: 下载代码 + +1. `mkdir -p /root/projects/paddle_models && cd /root/projects/paddle_models` +2. `git clone https://github.com/PaddlePaddle/models.git` + +`C++`预测代码在`/root/projects/paddle_models/models/PaddleCV/PaddleDetection/inference` 目录,该目录不依赖任何`PaddleDetection`下其他目录。 + + +### Step2: 下载PaddlePaddle C++ 预测库 fluid_inference + +目前仅支持`CUDA 8` 和 `CUDA 9`,请点击 [PaddlePaddle预测库下载地址](https://www.paddlepaddle.org.cn/documentation/docs/zh/develop/advanced_usage/deploy/inference/build_and_install_lib_cn.html)下载对应的版本(develop版本)。 + + +下载并解压后`/root/projects/fluid_inference`目录包含内容为: +``` +fluid_inference +├── paddle # paddle核心库和头文件 +| +├── third_party # 第三方依赖库和头文件 +| +└── version.txt # 版本和编译信息 +``` + +### Step3: 安装配置OpenCV + +```shell +# 0. 切换到/root/projects目录 +cd /root/projects +# 1. 下载OpenCV3.4.6版本源代码 +wget -c https://paddleseg.bj.bcebos.com/inference/opencv-3.4.6.zip +# 2. 解压 +unzip opencv-3.4.6.zip && cd opencv-3.4.6 +# 3. 创建build目录并编译, 这里安装到/usr/local/opencv3目录 +mkdir build && cd build +cmake .. -DCMAKE_INSTALL_PREFIX=/root/projects/opencv3 -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DWITH_IPP=OFF -DBUILD_IPP_IW=OFF -DWITH_LAPACK=OFF -DWITH_EIGEN=OFF -DCMAKE_INSTALL_LIBDIR=lib64 -DWITH_ZLIB=ON -DBUILD_ZLIB=ON -DWITH_JPEG=ON -DBUILD_JPEG=ON -DWITH_PNG=ON -DBUILD_PNG=ON -DWITH_TIFF=ON -DBUILD_TIFF=ON +make -j4 +make install +``` + +**注意:** 上述操作完成后,`opencv` 被安装在 `/root/projects/opencv3` 目录。 + +### Step4: 编译 + +`CMake`编译时,涉及到四个编译参数用于指定核心依赖库的路径, 他们的定义如下: + +| 参数名 | 含义 | +| ---- | ---- | +| CUDA_LIB | cuda的库路径 | +| CUDNN_LIB | cuDnn的库路径| +| OPENCV_DIR | OpenCV的安装路径, | +| PADDLE_DIR | Paddle预测库的路径 | + +执行下列操作时,**注意**把对应的参数改为你的上述依赖库实际路径: + +```shell +cd /root/projects/paddle_models/models/PaddleCV/PaddleDetection/inference + +mkdir build && cd build +cmake .. -DWITH_GPU=ON -DPADDLE_DIR=/root/projects/fluid_inference -DCUDA_LIB=/usr/local/cuda/lib64/ -DOPENCV_DIR=/root/projects/opencv3/ -DCUDNN_LIB=/usr/local/cuda/lib64/ +make +``` + + +### Step5: 预测及可视化 + +执行命令: + +``` +./detection_demo --conf=/path/to/your/conf --input_dir=/path/to/your/input/data/directory +``` + +更详细说明请参考ReadMe文档: [预测和可视化部分](../README.md) diff --git a/inference/docs/windows_vs2015_build.md b/inference/docs/windows_vs2015_build.md new file mode 100644 index 0000000000000000000000000000000000000000..be1c0289d404c17e561928173b104228ea63dbda --- /dev/null +++ b/inference/docs/windows_vs2015_build.md @@ -0,0 +1,97 @@ +# Windows平台使用 Visual Studio 2015 编译指南 + +本文档步骤,我们同时在`Visual Studio 2015` 和 `Visual Studio 2019 Community` 两个版本进行了测试,我们推荐使用[`Visual Studio 2019`直接编译`CMake`项目](./windows_vs2019_build.md)。 + + +## 前置条件 +* Visual Studio 2015 +* CUDA 8.0/ CUDA 9.0 +* CMake 3.0+ + +请确保系统已经安装好上述基本软件,**下面所有示例以工作目录为 `D:\projects`演示**。 + +### Step1: 下载代码 + +1. 打开`cmd`, 执行 `cd D:\projects\paddle_models` +2. `git clone https://github.com/PaddlePaddle/models.git` + +`C++`预测库代码在`D:\projects\paddle_models\models\PaddleCV\PaddleDetection\inference` 目录,该目录不依赖任何`PaddleDetection`下其他目录。 + + +### Step2: 下载PaddlePaddle C++ 预测库 fluid_inference + +根据Windows环境,下载相应版本的PaddlePaddle预测库,并解压到`D:\projects\`目录 + +| CUDA | GPU | 下载地址 | +|------|------|--------| +| 8.0 | Yes | [fluid_inference.zip](https://bj.bcebos.com/v1/paddleseg/fluid_inference_win.zip) | +| 9.0 | Yes | [fluid_inference_cuda90.zip](https://paddleseg.bj.bcebos.com/fluid_inference_cuda9_cudnn7.zip) | + +解压后`D:\projects\fluid_inference`目录包含内容为: +``` +fluid_inference +├── paddle # paddle核心库和头文件 +| +├── third_party # 第三方依赖库和头文件 +| +└── version.txt # 版本和编译信息 +``` + +### Step3: 安装配置OpenCV + +1. 在OpenCV官网下载适用于Windows平台的3.4.6版本, [下载地址](https://sourceforge.net/projects/opencvlibrary/files/3.4.6/opencv-3.4.6-vc14_vc15.exe/download) +2. 运行下载的可执行文件,将OpenCV解压至指定目录,如`D:\projects\opencv` +3. 配置环境变量,如下流程所示 + - 我的电脑->属性->高级系统设置->环境变量 + - 在系统变量中找到Path(如没有,自行创建),并双击编辑 + - 新建,将opencv路径填入并保存,如`D:\projects\opencv\build\x64\vc14\bin` + +### Step4: 以VS2015为例编译代码 + +以下命令需根据自己系统中各相关依赖的路径进行修改 + +* 调用VS2015, 请根据实际VS安装路径进行调整,打开cmd命令行工具执行以下命令 +* 其他vs版本(比如vs2019),请查找到对应版本的`vcvarsall.bat`路径,替换本命令即可 + +``` +call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 +``` + +* CMAKE编译工程 + * PADDLE_DIR: fluid_inference预测库路径 + * CUDA_LIB: CUDA动态库目录, 请根据实际安装情况调整 + * OPENCV_DIR: OpenCV解压目录 + +``` +# 切换到预测库所在目录 +cd /d D:\projects\paddle_models\models\PaddleCV\PaddleDetection\inference +# 创建构建目录, 重新构建只需要删除该目录即可 +mkdir build +cd build +# cmake构建VS项目 +D:\projects\paddle_models\models\PaddleCV\PaddleDetection\inference\build> cmake .. -G "Visual Studio 14 2015 Win64" -DWITH_GPU=ON -DPADDLE_DIR=D:\projects\fluid_inference -DCUDA_LIB=D:\projects\cudalib\v9.0\lib\x64 -DOPENCV_DIR=D:\projects\opencv -T host=x64 +``` + +这里的`cmake`参数`-G`, 表示生成对应的VS版本的工程,可以根据自己的`VS`版本调整,具体请参考[cmake文档](https://cmake.org/cmake/help/v3.15/manual/cmake-generators.7.html) + +* 生成可执行文件 + +``` +D:\projects\paddle_models\models\PaddleCV\PaddleDetection\inference\build> msbuild /m /p:Configuration=Release cpp_inference_demo.sln +``` + +### Step5: 预测及可视化 + +上述`Visual Studio 2015`编译产出的可执行文件在`build\release`目录下,切换到该目录: +``` +cd /d D:\projects\paddle_models\models\PaddleCV\PaddleDetection\inference\build\release +``` + +之后执行命令: + +``` +detection_demo.exe --conf=/path/to/your/conf --input_dir=/path/to/your/input/data/directory +``` + +更详细说明请参考ReadMe文档: [预测和可视化部分](../README.md) + diff --git a/inference/docs/windows_vs2019_build.md b/inference/docs/windows_vs2019_build.md new file mode 100644 index 0000000000000000000000000000000000000000..f3f589a9a246e494439b26f516ea319c270ff9ab --- /dev/null +++ b/inference/docs/windows_vs2019_build.md @@ -0,0 +1,102 @@ +# Visual Studio 2019 Community CMake 编译指南 + +Windows 平台下,我们使用`Visual Studio 2015` 和 `Visual Studio 2019 Community` 进行了测试。微软从`Visual Studio 2017`开始即支持直接管理`CMake`跨平台编译项目,但是直到`2019`才提供了稳定和完全的支持,所以如果你想使用CMake管理项目编译构建,我们推荐你使用`Visual Studio 2019`环境下构建。 + +你也可以使用和`VS2015`一样,通过把`CMake`项目转化成`VS`项目来编译,其中**有差别的部分**在文档中我们有说明,请参考:[使用Visual Studio 2015 编译指南](./windows_vs2015_build.md) + +## 前置条件 +* Visual Studio 2019 +* CUDA 8.0/ CUDA 9.0 +* CMake 3.0+ + +请确保系统已经安装好上述基本软件,我们使用的是`VS2019`的社区版。 + +**下面所有示例以工作目录为 `D:\projects`演示**。 + +### Step1: 下载代码 + +1. 点击下载源代码:[下载地址](https://github.com/PaddlePaddle/models/archive/develop.zip) +2. 解压,解压后目录重命名为`paddle_models` + +以下代码目录路径为`D:\projects\paddle_models` 为例。 + + +### Step2: 下载PaddlePaddle C++ 预测库 fluid_inference + +根据Windows环境,下载相应版本的PaddlePaddle预测库,并解压到`D:\projects\`目录 + +| CUDA | GPU | 下载地址 | +|------|------|--------| +| 8.0 | Yes | [fluid_inference.zip](https://bj.bcebos.com/v1/paddleseg/fluid_inference_win.zip) | +| 9.0 | Yes | [fluid_inference_cuda90.zip](https://paddleseg.bj.bcebos.com/fluid_inference_cuda9_cudnn7.zip) | + +解压后`D:\projects\fluid_inference`目录包含内容为: +``` +fluid_inference +├── paddle # paddle核心库和头文件 +| +├── third_party # 第三方依赖库和头文件 +| +└── version.txt # 版本和编译信息 +``` +**注意:** `CUDA90`版本解压后目录名称为`fluid_inference_cuda90`。 + +### Step3: 安装配置OpenCV + +1. 在OpenCV官网下载适用于Windows平台的3.4.6版本, [下载地址](https://sourceforge.net/projects/opencvlibrary/files/3.4.6/opencv-3.4.6-vc14_vc15.exe/download) +2. 运行下载的可执行文件,将OpenCV解压至指定目录,如`D:\projects\opencv` +3. 配置环境变量,如下流程所示 + - 我的电脑->属性->高级系统设置->环境变量 + - 在系统变量中找到Path(如没有,自行创建),并双击编辑 + - 新建,将opencv路径填入并保存,如`D:\projects\opencv\build\x64\vc14\bin` + +### Step4: 使用Visual Studio 2019直接编译CMake + +1. 打开Visual Studio 2019 Community,点击`继续但无需代码` +![step2](https://paddleseg.bj.bcebos.com/inference/vs2019_step1.png) +2. 点击: `文件`->`打开`->`CMake` +![step2.1](https://paddleseg.bj.bcebos.com/inference/vs2019_step2.png) + +选择项目代码所在路径,并打开`CMakeList.txt`: + +![step2.2](https://paddleseg.bj.bcebos.com/inference/vs2019_step3.png) + +3. 点击:`项目`->`cpp_inference_demo的CMake设置` + +![step3](https://paddleseg.bj.bcebos.com/inference/vs2019_step4.png) + +4. 点击`浏览`,分别设置编译选项指定`CUDA`、`OpenCV`、`Paddle预测库`的路径 + +![step4](https://paddleseg.bj.bcebos.com/inference/vs2019_step5.png) + +三个编译参数的含义说明如下: + +| 参数名 | 含义 | +| ---- | ---- | +| CUDA_LIB | cuda的库路径 | +| OPENCV_DIR | OpenCV的安装路径, | +| PADDLE_DIR | Paddle预测库的路径 | + +**设置完成后**, 点击上图中`保存并生成CMake缓存以加载变量`。 + +5. 点击`生成`->`全部生成` + +![step6](https://paddleseg.bj.bcebos.com/inference/vs2019_step6.png) + + +### Step5: 预测及可视化 + +上述`Visual Studio 2019`编译产出的可执行文件在`out\build\x64-Release`目录下,打开`cmd`,并切换到该目录: + +``` +cd D:\projects\paddle_models\models\PaddleCV\PaddleDetection\inference\build\x64-Release +``` + +之后执行命令: + +``` +detection_demo.exe --conf=/path/to/your/conf --input_dir=/path/to/your/input/data/directory +``` + +更详细说明请参考ReadMe文档: [预测和可视化部分](../README.md) + diff --git a/inference/external-cmake/yaml-cpp.cmake b/inference/external-cmake/yaml-cpp.cmake new file mode 100644 index 0000000000000000000000000000000000000000..15fa2674e00d85f1db7bbdfdceeebadaf0eabf5a --- /dev/null +++ b/inference/external-cmake/yaml-cpp.cmake @@ -0,0 +1,29 @@ + +find_package(Git REQUIRED) + +include(ExternalProject) + +message("${CMAKE_BUILD_TYPE}") + +ExternalProject_Add( + ext-yaml-cpp + GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git + GIT_TAG e0e01d53c27ffee6c86153fa41e7f5e57d3e5c90 + CMAKE_ARGS + -DYAML_CPP_BUILD_TESTS=OFF + -DYAML_CPP_BUILD_TOOLS=OFF + -DYAML_CPP_INSTALL=OFF + -DYAML_CPP_BUILD_CONTRIB=OFF + -DMSVC_SHARED_RT=OFF + -DBUILD_SHARED_LIBS=OFF + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS} + -DCMAKE_CXX_FLAGS_DEBUG=${CMAKE_CXX_FLAGS_DEBUG} + -DCMAKE_CXX_FLAGS_RELEASE=${CMAKE_CXX_FLAGS_RELEASE} + -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=${CMAKE_BINARY_DIR}/ext/yaml-cpp/lib + -DCMAKE_ARCHIVE_OUTPUT_DIRECTORY=${CMAKE_BINARY_DIR}/ext/yaml-cpp/lib + PREFIX "${CMAKE_BINARY_DIR}/ext/yaml-cpp" + # Disable install step + INSTALL_COMMAND "" + LOG_DOWNLOAD ON +) diff --git a/inference/images/detection_rcnn/000000014439.jpg b/inference/images/detection_rcnn/000000014439.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0abbdab06eb5950b93908cc91adfa640e8a3ac78 Binary files /dev/null and b/inference/images/detection_rcnn/000000014439.jpg differ diff --git a/inference/images/detection_rcnn/000000087038.jpg b/inference/images/detection_rcnn/000000087038.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9f77f5d5f057b6f92dc096da704ecb8dee99bdf5 Binary files /dev/null and b/inference/images/detection_rcnn/000000087038.jpg differ diff --git a/inference/images/detection_rcnn/000000570688.jpg b/inference/images/detection_rcnn/000000570688.jpg new file mode 100644 index 0000000000000000000000000000000000000000..cb304bd56c4010c08611a30dcca58ea9140cea54 Binary files /dev/null and b/inference/images/detection_rcnn/000000570688.jpg differ diff --git a/inference/predictor/detection_predictor.cpp b/inference/predictor/detection_predictor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ba07e3b6c7fb2152bd7825950a3cd94769f36adc --- /dev/null +++ b/inference/predictor/detection_predictor.cpp @@ -0,0 +1,383 @@ +// 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. + +#include "detection_predictor.h" +#include +#include +#include +#include "utils/detection_result.pb.h" + +namespace PaddleSolution { + /* lod_buffer: every item in lod_buffer is an image matrix after preprocessing + * input_buffer: same data with lod_buffer after flattening to 1-D vector and padding, needed to be empty before using this function + */ + void padding_minibatch(const std::vector> &lod_buffer, std::vector &input_buffer, + std::vector &resize_heights, std::vector &resize_widths, int channels, int coarsest_stride = 1) { + int batch_size = lod_buffer.size(); + int max_h = -1; + int max_w = -1; + for(int i = 0; i < batch_size; ++i) { + max_h = (max_h > resize_heights[i])? max_h:resize_heights[i]; + max_w = (max_w > resize_widths[i])? max_w:resize_widths[i]; + } + max_h = static_cast(ceil(static_cast(max_h) / static_cast(coarsest_stride)) * coarsest_stride); + max_w = static_cast(ceil(static_cast(max_w) / static_cast(coarsest_stride)) * coarsest_stride); + std::cout << "max_w: " << max_w << " max_h: " << max_h << std::endl; + input_buffer.insert(input_buffer.end(), batch_size * channels * max_h * max_w, 0); + // flatten tensor and padding + for(int i = 0; i < lod_buffer.size(); ++i) { + float *input_buffer_ptr = input_buffer.data() + i * channels * max_h * max_w; + const float *lod_ptr = lod_buffer[i].data(); + for(int c = 0; c < channels; ++c) { + for(int h = 0; h < resize_heights[i]; ++h) { + memcpy(input_buffer_ptr, lod_ptr, resize_widths[i] * sizeof(float)); + lod_ptr += resize_widths[i]; + input_buffer_ptr += max_w; + } + input_buffer_ptr += (max_h - resize_heights[i]) * max_w; + } + } + // change resize w, h + for(int i = 0; i < batch_size; ++i){ + resize_widths[i] = max_w; + resize_heights[i] = max_h; + } + } + + void output_detection_result(const float* out_addr, const std::vector> &lod_vector, const std::vector &imgs_batch){ + for(int i = 0; i < lod_vector[0].size() - 1; ++i) { + DetectionResult detection_result; + detection_result.set_filename(imgs_batch[i]); + std::cout << imgs_batch[i] << ":" << std::endl; + for (int j = lod_vector[0][i]; j < lod_vector[0][i+1]; ++j) { + DetectionBox *box_ptr = detection_result.add_detection_boxes(); + box_ptr->set_class_(static_cast(round(out_addr[0 + j * 6]))); + box_ptr->set_score(out_addr[1 + j * 6]); + box_ptr->set_left_top_x(out_addr[2 + j * 6]); + box_ptr->set_left_top_y(out_addr[3 + j * 6]); + box_ptr->set_right_bottom_x(out_addr[4 + j * 6]); + box_ptr->set_right_bottom_y(out_addr[5 + j * 6]); + printf("Class %d, score = %f, left top = [%f, %f], right bottom = [%f, %f]\n", + static_cast(round(out_addr[0 + j * 6])), out_addr[1 + j * 6], out_addr[2 + j * 6], + out_addr[3 + j * 6], out_addr[4 + j * 6], out_addr[5 + j * 6]); + } + printf("\n"); + std::ofstream output(imgs_batch[i] + ".pb", std::ios::out | std::ios::trunc | std::ios::binary); + detection_result.SerializeToOstream(&output); + output.close(); + } + } + + int DetectionPredictor::init(const std::string& conf) { + if (!_model_config.load_config(conf)) { + LOG(FATAL) << "Fail to load config file: [" << conf << "]"; + return -1; + } + _preprocessor = PaddleSolution::create_processor(conf); + if (_preprocessor == nullptr) { + LOG(FATAL) << "Failed to create_processor"; + return -1; + } + + bool use_gpu = _model_config._use_gpu; + const auto& model_dir = _model_config._model_path; + const auto& model_filename = _model_config._model_file_name; + const auto& params_filename = _model_config._param_file_name; + + // load paddle model file + if (_model_config._predictor_mode == "NATIVE") { + paddle::NativeConfig config; + auto prog_file = utils::path_join(model_dir, model_filename); + auto param_file = utils::path_join(model_dir, params_filename); + config.prog_file = prog_file; + config.param_file = param_file; + config.fraction_of_gpu_memory = 0; + config.use_gpu = use_gpu; + config.device = 0; + _main_predictor = paddle::CreatePaddlePredictor(config); + } else if (_model_config._predictor_mode == "ANALYSIS") { + paddle::AnalysisConfig config; + if (use_gpu) { + config.EnableUseGpu(100, 0); + } + auto prog_file = utils::path_join(model_dir, model_filename); + auto param_file = utils::path_join(model_dir, params_filename); + config.SetModel(prog_file, param_file); + config.SwitchUseFeedFetchOps(false); + config.SwitchSpecifyInputNames(true); + config.EnableMemoryOptim(); + _main_predictor = paddle::CreatePaddlePredictor(config); + } else { + return -1; + } + return 0; + + } + + int DetectionPredictor::predict(const std::vector& imgs) { + if (_model_config._predictor_mode == "NATIVE") { + return native_predict(imgs); + } + else if (_model_config._predictor_mode == "ANALYSIS") { + return analysis_predict(imgs); + } + return -1; + } + + int DetectionPredictor::native_predict(const std::vector& imgs) { + int config_batch_size = _model_config._batch_size; + + int channels = _model_config._channels; + int eval_width = _model_config._resize[0]; + int eval_height = _model_config._resize[1]; + std::size_t total_size = imgs.size(); + int default_batch_size = std::min(config_batch_size, (int)total_size); + int batch = total_size / default_batch_size + ((total_size % default_batch_size) != 0); + int batch_buffer_size = default_batch_size * channels * eval_width * eval_height; + + auto& input_buffer = _buffer; + auto& imgs_batch = _imgs_batch; + float sr; + // DetectionResultsContainer result_container; + for (int u = 0; u < batch; ++u) { + int batch_size = default_batch_size; + if (u == (batch - 1) && (total_size % default_batch_size)) { + batch_size = total_size % default_batch_size; + } + + int real_buffer_size = batch_size * channels * eval_width * eval_height; + std::vector feeds; + input_buffer.clear(); + imgs_batch.clear(); + for (int i = 0; i < batch_size; ++i) { + int idx = u * default_batch_size + i; + imgs_batch.push_back(imgs[idx]); + } + std::vector ori_widths; + std::vector ori_heights; + std::vector resize_widths; + std::vector resize_heights; + std::vector scale_ratios; + ori_widths.resize(batch_size); + ori_heights.resize(batch_size); + resize_widths.resize(batch_size); + resize_heights.resize(batch_size); + scale_ratios.resize(batch_size); + std::vector> lod_buffer(batch_size); + if (!_preprocessor->batch_process(imgs_batch, lod_buffer, ori_widths.data(), ori_heights.data(), + resize_widths.data(), resize_heights.data(), scale_ratios.data())) { + return -1; + } + // flatten and padding + padding_minibatch(lod_buffer, input_buffer, resize_heights, resize_widths, channels, _model_config._coarsest_stride); + paddle::PaddleTensor im_tensor, im_size_tensor, im_info_tensor; + + im_tensor.name = "image"; + im_tensor.shape = std::vector({ batch_size, channels, resize_heights[0], resize_widths[0] }); + im_tensor.data.Reset(input_buffer.data(), input_buffer.size() * sizeof(float)); + im_tensor.dtype = paddle::PaddleDType::FLOAT32; + + std::vector image_infos; + for(int i = 0; i < batch_size; ++i) { + image_infos.push_back(resize_heights[i]); + image_infos.push_back(resize_widths[i]); + image_infos.push_back(scale_ratios[i]); + } + im_info_tensor.name = "info"; + im_info_tensor.shape = std::vector({batch_size, 3}); + im_info_tensor.data.Reset(image_infos.data(), batch_size * 3 * sizeof(float)); + im_info_tensor.dtype = paddle::PaddleDType::FLOAT32; + + std::vector image_size; + for(int i = 0; i < batch_size; ++i) { + image_size.push_back(ori_heights[i]); + image_size.push_back(ori_widths[i]); + } + + std::vector image_size_f; + for(int i = 0; i < batch_size; ++i) { + image_size_f.push_back(ori_heights[i]); + image_size_f.push_back(ori_widths[i]); + image_size_f.push_back(1.0); + } + + int feeds_size = _model_config._feeds_size; + im_size_tensor.name = "im_size"; + if(feeds_size == 2) { + im_size_tensor.shape = std::vector({ batch_size, 2}); + im_size_tensor.data.Reset(image_size.data(), batch_size * 2 * sizeof(int)); + im_size_tensor.dtype = paddle::PaddleDType::INT32; + } + else if(feeds_size == 3) { + im_size_tensor.shape = std::vector({ batch_size, 3}); + im_size_tensor.data.Reset(image_size_f.data(), batch_size * 3 * sizeof(float)); + im_size_tensor.dtype = paddle::PaddleDType::FLOAT32; + } + std::cout << "Feed size = " << feeds_size << std::endl; + feeds.push_back(im_tensor); + if(_model_config._feeds_size > 2) { + feeds.push_back(im_info_tensor); + } + feeds.push_back(im_size_tensor); + _outputs.clear(); + + auto t1 = std::chrono::high_resolution_clock::now(); + if (!_main_predictor->Run(feeds, &_outputs, batch_size)) { + LOG(ERROR) << "Failed: NativePredictor->Run() return false at batch: " << u; + continue; + } + auto t2 = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(t2 - t1).count(); + std::cout << "runtime = " << duration << std::endl; + std::cout << "Number of outputs:" << _outputs.size() << std::endl; + int out_num = 1; + // print shape of first output tensor for debugging + std::cout << "size of outputs[" << 0 << "]: ("; + for (int j = 0; j < _outputs[0].shape.size(); ++j) { + out_num *= _outputs[0].shape[j]; + std::cout << _outputs[0].shape[j] << ","; + } + std::cout << ")" << std::endl; + + // const size_t nums = _outputs.front().data.length() / sizeof(float); + // if (out_num % batch_size != 0 || out_num != nums) { + // LOG(ERROR) << "outputs data size mismatch with shape size."; + // return -1; + // } + float* out_addr = (float *)(_outputs[0].data.data()); + output_detection_result(out_addr, _outputs[0].lod, imgs_batch); + } + return 0; + } + + int DetectionPredictor::analysis_predict(const std::vector& imgs) { + + int config_batch_size = _model_config._batch_size; + int channels = _model_config._channels; + int eval_width = _model_config._resize[0]; + int eval_height = _model_config._resize[1]; + auto total_size = imgs.size(); + int default_batch_size = std::min(config_batch_size, (int)total_size); + int batch = total_size / default_batch_size + ((total_size % default_batch_size) != 0); + int batch_buffer_size = default_batch_size * channels * eval_width * eval_height; + + auto& input_buffer = _buffer; + auto& imgs_batch = _imgs_batch; + //DetectionResultsContainer result_container; + for (int u = 0; u < batch; ++u) { + int batch_size = default_batch_size; + if (u == (batch - 1) && (total_size % default_batch_size)) { + batch_size = total_size % default_batch_size; + } + + int real_buffer_size = batch_size * channels * eval_width * eval_height; + std::vector feeds; + //input_buffer.resize(real_buffer_size); + input_buffer.clear(); + imgs_batch.clear(); + for (int i = 0; i < batch_size; ++i) { + int idx = u * default_batch_size + i; + imgs_batch.push_back(imgs[idx]); + } + + std::vector ori_widths; + std::vector ori_heights; + std::vector resize_widths; + std::vector resize_heights; + std::vector scale_ratios; + ori_widths.resize(batch_size); + ori_heights.resize(batch_size); + resize_widths.resize(batch_size); + resize_heights.resize(batch_size); + scale_ratios.resize(batch_size); + + std::vector> lod_buffer(batch_size); + if (!_preprocessor->batch_process(imgs_batch, lod_buffer, ori_widths.data(), ori_heights.data(), + resize_widths.data(), resize_heights.data(), scale_ratios.data())){ + std::cout << "Failed to preprocess!" << std::endl; + return -1; + } + + //flatten tensor + padding_minibatch(lod_buffer, input_buffer, resize_heights, resize_widths, channels, _model_config._coarsest_stride); + + std::vector input_names = _main_predictor->GetInputNames(); + auto im_tensor = _main_predictor->GetInputTensor(input_names.front()); + im_tensor->Reshape({ batch_size, channels, resize_heights[0], resize_widths[0] }); + im_tensor->copy_from_cpu(input_buffer.data()); + + if(input_names.size() > 2){ + std::vector image_infos; + for(int i = 0; i < batch_size; ++i) { + image_infos.push_back(resize_heights[i]); + image_infos.push_back(resize_widths[i]); + image_infos.push_back(scale_ratios[i]); + } + auto im_info_tensor = _main_predictor->GetInputTensor(input_names[1]); + im_info_tensor->Reshape({batch_size, 3}); + im_info_tensor->copy_from_cpu(image_infos.data()); + } + + std::vector image_size; + for(int i = 0; i < batch_size; ++i) { + image_size.push_back(ori_heights[i]); + image_size.push_back(ori_widths[i]); + } + std::vector image_size_f; + for(int i = 0; i < batch_size; ++i) { + image_size_f.push_back(static_cast(ori_heights[i])); + image_size_f.push_back(static_cast(ori_widths[i])); + image_size_f.push_back(1.0); + } + + auto im_size_tensor = _main_predictor->GetInputTensor(input_names.back()); + if(input_names.size() > 2) { + im_size_tensor->Reshape({batch_size, 3}); + im_size_tensor->copy_from_cpu(image_size_f.data()); + } + else{ + im_size_tensor->Reshape({batch_size, 2}); + im_size_tensor->copy_from_cpu(image_size.data()); + } + + + auto t1 = std::chrono::high_resolution_clock::now(); + _main_predictor->ZeroCopyRun(); + auto t2 = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(t2 - t1).count(); + std::cout << "runtime = " << duration << std::endl; + + auto output_names = _main_predictor->GetOutputNames(); + auto output_t = _main_predictor->GetOutputTensor(output_names[0]); + std::vector out_data; + std::vector output_shape = output_t->shape(); + + int out_num = 1; + std::cout << "size of outputs[" << 0 << "]: ("; + for (int j = 0; j < output_shape.size(); ++j) { + out_num *= output_shape[j]; + std::cout << output_shape[j] << ","; + } + std::cout << ")" << std::endl; + + out_data.resize(out_num); + output_t->copy_to_cpu(out_data.data()); + + float* out_addr = (float *)(out_data.data()); + auto lod_vector = output_t->lod(); + output_detection_result(out_addr, lod_vector, imgs_batch); + } + return 0; + } +} diff --git a/inference/predictor/detection_predictor.h b/inference/predictor/detection_predictor.h new file mode 100644 index 0000000000000000000000000000000000000000..3bc4cfdd793291d7d89342c7fbccfdd558d1f004 --- /dev/null +++ b/inference/predictor/detection_predictor.h @@ -0,0 +1,52 @@ +// 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. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace PaddleSolution { + class DetectionPredictor { + public: + // init a predictor with a yaml config file + int init(const std::string& conf); + // predict api + int predict(const std::vector& imgs); + + private: + int native_predict(const std::vector& imgs); + int analysis_predict(const std::vector& imgs); + private: + std::vector _buffer; + std::vector _imgs_batch; + std::vector _outputs; + + PaddleSolution::PaddleModelConfigPaser _model_config; + std::shared_ptr _preprocessor; + std::unique_ptr _main_predictor; + }; +} diff --git a/inference/preprocessor/preprocessor.cpp b/inference/preprocessor/preprocessor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dbe7bcf624b649c02297bddd593d173b57550f17 --- /dev/null +++ b/inference/preprocessor/preprocessor.cpp @@ -0,0 +1,43 @@ +// 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. + +#include + +#include "preprocessor.h" +#include "preprocessor_detection.h" + +namespace PaddleSolution { + + std::shared_ptr create_processor(const std::string& conf_file) { + + auto config = std::make_shared(); + if (!config->load_config(conf_file)) { + LOG(FATAL) << "fail to laod conf file [" << conf_file << "]"; + return nullptr; + } + + if (config->_pre_processor == "DetectionPreProcessor") { + auto p = std::make_shared(); + if (!p->init(config)) { + return nullptr; + } + return p; + } + + + LOG(FATAL) << "unknown processor_name [" << config->_pre_processor << "]"; + + return nullptr; + } +} diff --git a/inference/preprocessor/preprocessor.h b/inference/preprocessor/preprocessor.h new file mode 100644 index 0000000000000000000000000000000000000000..a3fb2e029c8acf92010a258dd2824b85a0f2f90f --- /dev/null +++ b/inference/preprocessor/preprocessor.h @@ -0,0 +1,64 @@ +// 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. + +#pragma once +#include +#include +#include + +#include +#include +#include + +#include "utils/conf_parser.h" + +namespace PaddleSolution { + +class ImagePreProcessor { +protected: + ImagePreProcessor() {}; + +public: + virtual ~ImagePreProcessor() {} + + virtual bool single_process(const std::string& fname, float* data, int* ori_w, int* ori_h) { + return true; + } + + virtual bool batch_process(const std::vector& imgs, float* data, int* ori_w, int* ori_h) { + return true; + } + + virtual bool single_process(const std::string& fname, float* data) { + return true; + } + + virtual bool batch_process(const std::vector& imgs, float* data) { + return true; + } + + virtual bool single_process(const std::string& fname, std::vector &data, int* ori_w, int* ori_h, int* resize_w, int* resize_h, float* scale_ratio) { + return true; + } + + virtual bool batch_process(const std::vector& imgs, std::vector> &data, int* ori_w, int* ori_h, int* resize_w, int* resize_h, float* scale_ratio) { + return true; + } + +}; // end of class ImagePreProcessor + +std::shared_ptr create_processor(const std::string &config_file); + +} // end of namespace paddle_solution + diff --git a/inference/preprocessor/preprocessor_detection.cpp b/inference/preprocessor/preprocessor_detection.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ba8fd0e328c5a859e2d4b88adba0e56e5e3a7476 --- /dev/null +++ b/inference/preprocessor/preprocessor_detection.cpp @@ -0,0 +1,130 @@ +// 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. + +#include +#include + +#include + +#include "preprocessor_detection.h" +#include "utils/utils.h" + +namespace PaddleSolution { + bool DetectionPreProcessor::single_process(const std::string& fname, std::vector &vec_data, int* ori_w, int* ori_h, int* resize_w, int* resize_h, float* scale_ratio) { + cv::Mat im1 = cv::imread(fname, -1); + cv::Mat im; + if(_config->_feeds_size == 3) { // faster rcnn + im1.convertTo(im, CV_32FC3, 1/255.0); + } + else if(_config->_feeds_size == 2){ //yolo v3 + im = im1; + } + if (im.data == nullptr || im.empty()) { + LOG(ERROR) << "Failed to open image: " << fname; + return false; + } + + int channels = im.channels(); + if (channels == 1) { + cv::cvtColor(im, im, cv::COLOR_GRAY2BGR); + } + channels = im.channels(); + if (channels != 3 && channels != 4) { + LOG(ERROR) << "Only support rgb(gray) and rgba image."; + return false; + } + *ori_w = im.cols; + *ori_h = im.rows; + cv::cvtColor(im, im, cv::COLOR_BGR2RGB); + //channels = im.channels(); + + //resize + int rw = im.cols; + int rh = im.rows; + float im_scale_ratio; + utils::scaling(_config->_resize_type, rw, rh, _config->_resize[0], _config->_resize[1], _config->_target_short_size, _config->_resize_max_size, im_scale_ratio); + cv::Size resize_size(rw, rh); + *resize_w = rw; + *resize_h = rh; + *scale_ratio = im_scale_ratio; + if (*ori_h != rh || *ori_w != rw) { + cv::Mat im_temp; + if(_config->_resize_type == utils::SCALE_TYPE::UNPADDING) { + cv::resize(im, im_temp, resize_size, 0, 0, cv::INTER_LINEAR); + } + else if(_config->_resize_type == utils::SCALE_TYPE::RANGE_SCALING) { + cv::resize(im, im_temp, cv::Size(), im_scale_ratio, im_scale_ratio, cv::INTER_LINEAR); + } + im = im_temp; + } + + vec_data.resize(channels * rw * rh); + float *data = vec_data.data(); + + float* pmean = _config->_mean.data(); + float* pscale = _config->_std.data(); + for (int h = 0; h < rh; ++h) { + const uchar* uptr = im.ptr(h); + const float* fptr = im.ptr(h); + int im_index = 0; + for (int w = 0; w < rw; ++w) { + for (int c = 0; c < channels; ++c) { + int top_index = (c * rh + h) * rw + w; + float pixel;// = static_cast(fptr[im_index]);// / 255.0; + if(_config->_feeds_size == 2){ //yolo v3 + pixel = static_cast(uptr[im_index++]) / 255.0; + } + else if(_config->_feeds_size == 3){ + pixel = fptr[im_index++]; + } + pixel = (pixel - pmean[c]) / pscale[c]; + data[top_index] = pixel; + } + } + } + return true; + } + + bool DetectionPreProcessor::batch_process(const std::vector& imgs, std::vector> &data, int* ori_w, int* ori_h, int* resize_w, int* resize_h, float* scale_ratio) { + auto ic = _config->_channels; + auto iw = _config->_resize[0]; + auto ih = _config->_resize[1]; + std::vector threads; + for (int i = 0; i < imgs.size(); ++i) { + std::string path = imgs[i]; + int* width = &ori_w[i]; + int* height = &ori_h[i]; + int* resize_width = &resize_w[i]; + int* resize_height = &resize_h[i]; + float* sr = &scale_ratio[i]; + threads.emplace_back([this, &data, i, path, width, height, resize_width, resize_height, sr] { + std::vector buffer; + single_process(path, buffer, width, height, resize_width, resize_height, sr); + data[i] = buffer; + }); + } + for (auto& t : threads) { + if (t.joinable()) { + t.join(); + } + } + return true; + } + + bool DetectionPreProcessor::init(std::shared_ptr config) { + _config = config; + return true; + } + +} diff --git a/inference/preprocessor/preprocessor_detection.h b/inference/preprocessor/preprocessor_detection.h new file mode 100644 index 0000000000000000000000000000000000000000..731329040423756151a2590d3ed0f46b2800191d --- /dev/null +++ b/inference/preprocessor/preprocessor_detection.h @@ -0,0 +1,36 @@ +// 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. + +#pragma once + +#include "preprocessor.h" + +namespace PaddleSolution { + + class DetectionPreProcessor : public ImagePreProcessor { + + public: + DetectionPreProcessor() : _config(nullptr) { + }; + + bool init(std::shared_ptr config); + + bool single_process(const std::string& fname, std::vector &data, int* ori_w, int* ori_h, int* resize_w, int* resize_h, float* scale_ratio); + + bool batch_process(const std::vector& imgs, std::vector> &data, int* ori_w, int* ori_h, int* resize_w, int* resize_h, float* scale_ratio); + private: + std::shared_ptr _config; + }; + +} diff --git a/inference/tools/coco17.json b/inference/tools/coco17.json new file mode 100644 index 0000000000000000000000000000000000000000..d3bbbaad038534baacf6f86f78db5d32bce16238 --- /dev/null +++ b/inference/tools/coco17.json @@ -0,0 +1,83 @@ +{ + "0" : "background", + "1" : "person", + "2" : "bicycle", + "3" : "car", + "4" : "motorcycle", + "5" : "airplane", + "6" : "bus", + "7" : "train", + "8" : "truck", + "9" : "boat", + "10" : "traffic light", + "11" : "fire hydrant", + "12" : "stop sign", + "13" : "parking meter", + "14" : "bench", + "15" : "bird", + "16" : "cat", + "17" : "dog", + "18" : "horse", + "19" : "sheep", + "20" : "cow", + "21" : "elephant", + "22" : "bear", + "23" : "zebra", + "24" : "giraffe", + "25" : "backpack", + "26" : "umbrella", + "27" : "handbag", + "28" : "tie", + "29" : "suitcase", + "30" : "frisbee", + "31" : "skis", + "32" : "snowboard", + "33" : "sports ball", + "34" : "kite", + "35" : "baseball bat", + "36" : "baseball glove", + "37" : "skateboard", + "38" : "surfboard", + "39" : "tennis racket", + "40" : "bottle", + "41" : "wine glass", + "42" : "cup", + "43" : "fork", + "44" : "knife", + "45" : "spoon", + "46" : "bowl", + "47" : "banana", + "48" : "apple", + "49" : "sandwich", + "50" : "orange", + "51" : "broccoli", + "52" : "carrot", + "53" : "hot dog", + "54" : "pizza", + "55" : "donut", + "56" : "cake", + "57" : "chair", + "58" : "couch", + "59" : "potted plant", + "60" : "bed", + "61" : "dining table", + "62" : "toilet", + "63" : "tv", + "64" : "laptop", + "65" : "mouse", + "66" : "remote", + "67" : "keyboard", + "68" : "cell phone", + "69" : "microwave", + "70" : "oven", + "71" : "toaster", + "72" : "sink", + "73" : "refrigerator", + "74" : "book", + "75" : "clock", + "76" : "vase", + "77" : "scissors", + "78" : "teddy bear", + "79" : "hair drier", + "80" : "toothbrush" +} diff --git a/inference/tools/detection_result_pb2.py b/inference/tools/detection_result_pb2.py new file mode 100644 index 0000000000000000000000000000000000000000..3dc66f368b6fea72f70d6a5685b19f23e8021d51 --- /dev/null +++ b/inference/tools/detection_result_pb2.py @@ -0,0 +1,151 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: detection_result.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='detection_result.proto', + package='PaddleSolution', + syntax='proto2', + serialized_pb=_b('\n\x16\x64\x65tection_result.proto\x12\x0ePaddleSolution\"\x84\x01\n\x0c\x44\x65tectionBox\x12\r\n\x05\x63lass\x18\x01 \x01(\x05\x12\r\n\x05score\x18\x02 \x01(\x02\x12\x12\n\nleft_top_x\x18\x03 \x01(\x02\x12\x12\n\nleft_top_y\x18\x04 \x01(\x02\x12\x16\n\x0eright_bottom_x\x18\x05 \x01(\x02\x12\x16\n\x0eright_bottom_y\x18\x06 \x01(\x02\"Z\n\x0f\x44\x65tectionResult\x12\x10\n\x08\x66ilename\x18\x01 \x01(\t\x12\x35\n\x0f\x64\x65tection_boxes\x18\x02 \x03(\x0b\x32\x1c.PaddleSolution.DetectionBox') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + + + + +_DETECTIONBOX = _descriptor.Descriptor( + name='DetectionBox', + full_name='PaddleSolution.DetectionBox', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='class', full_name='PaddleSolution.DetectionBox.class', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='score', full_name='PaddleSolution.DetectionBox.score', index=1, + number=2, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='left_top_x', full_name='PaddleSolution.DetectionBox.left_top_x', index=2, + number=3, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='left_top_y', full_name='PaddleSolution.DetectionBox.left_top_y', index=3, + number=4, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='right_bottom_x', full_name='PaddleSolution.DetectionBox.right_bottom_x', index=4, + number=5, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='right_bottom_y', full_name='PaddleSolution.DetectionBox.right_bottom_y', index=5, + number=6, type=2, cpp_type=6, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=43, + serialized_end=175, +) + + +_DETECTIONRESULT = _descriptor.Descriptor( + name='DetectionResult', + full_name='PaddleSolution.DetectionResult', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='filename', full_name='PaddleSolution.DetectionResult.filename', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=_b("").decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='detection_boxes', full_name='PaddleSolution.DetectionResult.detection_boxes', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=177, + serialized_end=267, +) + +_DETECTIONRESULT.fields_by_name['detection_boxes'].message_type = _DETECTIONBOX +DESCRIPTOR.message_types_by_name['DetectionBox'] = _DETECTIONBOX +DESCRIPTOR.message_types_by_name['DetectionResult'] = _DETECTIONRESULT + +DetectionBox = _reflection.GeneratedProtocolMessageType('DetectionBox', (_message.Message,), dict( + DESCRIPTOR = _DETECTIONBOX, + __module__ = 'detection_result_pb2' + # @@protoc_insertion_point(class_scope:PaddleSolution.DetectionBox) + )) +_sym_db.RegisterMessage(DetectionBox) + +DetectionResult = _reflection.GeneratedProtocolMessageType('DetectionResult', (_message.Message,), dict( + DESCRIPTOR = _DETECTIONRESULT, + __module__ = 'detection_result_pb2' + # @@protoc_insertion_point(class_scope:PaddleSolution.DetectionResult) + )) +_sym_db.RegisterMessage(DetectionResult) + + +# @@protoc_insertion_point(module_scope) diff --git a/inference/tools/vis.py b/inference/tools/vis.py new file mode 100644 index 0000000000000000000000000000000000000000..1ca13bfbaf48669a78bf94344d378c37fe071f1a --- /dev/null +++ b/inference/tools/vis.py @@ -0,0 +1,104 @@ +# coding: utf-8 +# 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 detection_result_pb2 +import cv2 +import sys +import gflags +import numpy as np +import json +from PIL import Image, ImageDraw, ImageFont + +Flags = gflags.FLAGS +gflags.DEFINE_string('img_path', 'abc', 'image path') +gflags.DEFINE_string('img_result_path', 'def', 'image result path') +gflags.DEFINE_float('threshold', 0.0, 'threshold of score') +gflags.DEFINE_string('c2l_path', 'ghk', 'class to label path') + +def colormap(rgb=False): + """ + Get colormap + """ + color_list = np.array([ + 0.000, 0.447, 0.741, 0.850, 0.325, 0.098, 0.929, 0.694, 0.125, 0.494, + 0.184, 0.556, 0.466, 0.674, 0.188, 0.301, 0.745, 0.933, 0.635, 0.078, + 0.184, 0.300, 0.300, 0.300, 0.600, 0.600, 0.600, 1.000, 0.000, 0.000, + 1.000, 0.500, 0.000, 0.749, 0.749, 0.000, 0.000, 1.000, 0.000, 0.000, + 0.000, 1.000, 0.667, 0.000, 1.000, 0.333, 0.333, 0.000, 0.333, 0.667, + 0.000, 0.333, 1.000, 0.000, 0.667, 0.333, 0.000, 0.667, 0.667, 0.000, + 0.667, 1.000, 0.000, 1.000, 0.333, 0.000, 1.000, 0.667, 0.000, 1.000, + 1.000, 0.000, 0.000, 0.333, 0.500, 0.000, 0.667, 0.500, 0.000, 1.000, + 0.500, 0.333, 0.000, 0.500, 0.333, 0.333, 0.500, 0.333, 0.667, 0.500, + 0.333, 1.000, 0.500, 0.667, 0.000, 0.500, 0.667, 0.333, 0.500, 0.667, + 0.667, 0.500, 0.667, 1.000, 0.500, 1.000, 0.000, 0.500, 1.000, 0.333, + 0.500, 1.000, 0.667, 0.500, 1.000, 1.000, 0.500, 0.000, 0.333, 1.000, + 0.000, 0.667, 1.000, 0.000, 1.000, 1.000, 0.333, 0.000, 1.000, 0.333, + 0.333, 1.000, 0.333, 0.667, 1.000, 0.333, 1.000, 1.000, 0.667, 0.000, + 1.000, 0.667, 0.333, 1.000, 0.667, 0.667, 1.000, 0.667, 1.000, 1.000, + 1.000, 0.000, 1.000, 1.000, 0.333, 1.000, 1.000, 0.667, 1.000, 0.167, + 0.000, 0.000, 0.333, 0.000, 0.000, 0.500, 0.000, 0.000, 0.667, 0.000, + 0.000, 0.833, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 0.167, 0.000, + 0.000, 0.333, 0.000, 0.000, 0.500, 0.000, 0.000, 0.667, 0.000, 0.000, + 0.833, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 0.167, 0.000, 0.000, + 0.333, 0.000, 0.000, 0.500, 0.000, 0.000, 0.667, 0.000, 0.000, 0.833, + 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 0.143, 0.143, 0.143, 0.286, + 0.286, 0.286, 0.429, 0.429, 0.429, 0.571, 0.571, 0.571, 0.714, 0.714, + 0.714, 0.857, 0.857, 0.857, 1.000, 1.000, 1.000 + ]).astype(np.float32) + color_list = color_list.reshape((-1, 3)) * 255 + if not rgb: + color_list = color_list[:, ::-1] + return color_list + +if __name__ == "__main__": + if len(sys.argv) != 5: + print("Usage: python vis.py --img_path=/path/to/image --img_result_path=/path/to/image_result.pb --threshold=0.1 --c2l_path=/path/to/class2label.json") + else: + Flags(sys.argv) + color_list = colormap(rgb=True) + text_thickness = 1 + text_scale = 0.3 + with open(Flags.img_result_path, "rb") as f: + detection_result = detection_result_pb2.DetectionResult() + detection_result.ParseFromString(f.read()) + img = cv2.imread(Flags.img_path) + img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) + class2LabelMap = dict() + with open(Flags.c2l_path, "r", encoding="utf-8") as json_f: + class2LabelMap = json.load(json_f) + for box in detection_result.detection_boxes: + if box.score >= Flags.threshold: + box_class = getattr(box, 'class') + text_class_score_str = "%s %.2f" % (class2LabelMap.get(str(box_class)), box.score) + text_point = (int(box.left_top_x), int(box.left_top_y)) + + ptLeftTop = (int(box.left_top_x), int(box.left_top_y)) + ptRightBottom = (int(box.right_bottom_x), int(box.right_bottom_y)) + box_thickness = 1 + color = tuple([int(c) for c in color_list[box_class]]) + cv2.rectangle(img, ptLeftTop, ptRightBottom, color, box_thickness, 8) + if text_point[1] < 0: + text_point = (int(box.left_top_x), int(box.right_bottom_y)) + WHITE = (255, 255, 255) + font = cv2.FONT_HERSHEY_SIMPLEX + text_size = cv2.getTextSize(text_class_score_str, font, text_scale, text_thickness) + + text_box_left_top = (text_point[0], text_point[1] - text_size[0][1]) + text_box_right_bottom = (text_point[0] + text_size[0][0], text_point[1]) + + cv2.rectangle(img, text_box_left_top, text_box_right_bottom, color, -1, 8) + cv2.putText(img, text_class_score_str, text_point, font, text_scale, WHITE, text_thickness) + img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) + cv2.imwrite(Flags.img_path + ".png", img) diff --git a/inference/utils/conf_parser.h b/inference/utils/conf_parser.h new file mode 100644 index 0000000000000000000000000000000000000000..21944d032b2c24cdb584dc076a696560d4665ea1 --- /dev/null +++ b/inference/utils/conf_parser.h @@ -0,0 +1,237 @@ +// 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. + +#pragma once +#include +#include +#include +#include + +#include +namespace PaddleSolution { + + class PaddleModelConfigPaser { + std::map _scaling_map; + public: + PaddleModelConfigPaser() + :_class_num(0), + _channels(0), + _use_gpu(0), + _batch_size(1), + _target_short_size(0), + _model_file_name("__model__"), + _param_file_name("__params__"), + _scaling_map{{"UNPADDING", 0}, + {"RANGE_SCALING",1}}, + _feeds_size(1), + _coarsest_stride(1) + { + } + ~PaddleModelConfigPaser() { + } + + void reset() { + _crop_size.clear(); + _resize.clear(); + _mean.clear(); + _std.clear(); + _img_type.clear(); + _class_num = 0; + _channels = 0; + _use_gpu = 0; + _target_short_size = 0; + _batch_size = 1; + _model_file_name = "__model__"; + _model_path = "./"; + _param_file_name="__params__"; + _resize_type = 0; + _resize_max_size = 0; + _feeds_size = 1; + _coarsest_stride = 1; + } + + std::string process_parenthesis(const std::string& str) { + if (str.size() < 2) { + return str; + } + std::string nstr(str); + if (str[0] == '(' && str.back() == ')') { + nstr[0] = '['; + nstr[str.size() - 1] = ']'; + } + return nstr; + } + + template + std::vector parse_str_to_vec(const std::string& str) { + std::vector data; + auto node = YAML::Load(str); + for (const auto& item : node) { + data.push_back(item.as()); + } + return data; + } + + bool load_config(const std::string& conf_file) { + + reset(); + + YAML::Node config = YAML::LoadFile(conf_file); + // 1. get resize + auto str = config["DEPLOY"]["EVAL_CROP_SIZE"].as(); + _resize = parse_str_to_vec(process_parenthesis(str)); + + // 0. get crop_size + if(config["DEPLOY"]["CROP_SIZE"].IsDefined()) { + auto crop_str = config["DEPLOY"]["CROP_SIZE"].as(); + _crop_size = parse_str_to_vec(process_parenthesis(crop_str)); + } + else { + _crop_size = _resize; + } + + // 2. get mean + for (const auto& item : config["DEPLOY"]["MEAN"]) { + _mean.push_back(item.as()); + } + + // 3. get std + for (const auto& item : config["DEPLOY"]["STD"]) { + _std.push_back(item.as()); + } + + // 4. get image type + _img_type = config["DEPLOY"]["IMAGE_TYPE"].as(); + // 5. get class number + _class_num = config["DEPLOY"]["NUM_CLASSES"].as(); + // 7. set model path + _model_path = config["DEPLOY"]["MODEL_PATH"].as(); + // 8. get model file_name + _model_file_name = config["DEPLOY"]["MODEL_FILENAME"].as(); + // 9. get model param file name + _param_file_name = config["DEPLOY"]["PARAMS_FILENAME"].as(); + // 10. get pre_processor + _pre_processor = config["DEPLOY"]["PRE_PROCESSOR"].as(); + // 11. use_gpu + _use_gpu = config["DEPLOY"]["USE_GPU"].as(); + // 12. predictor_mode + _predictor_mode = config["DEPLOY"]["PREDICTOR_MODE"].as(); + // 13. batch_size + _batch_size = config["DEPLOY"]["BATCH_SIZE"].as(); + // 14. channels + _channels = config["DEPLOY"]["CHANNELS"].as(); + // 15. target_short_size + if(config["DEPLOY"]["TARGET_SHORT_SIZE"].IsDefined()) { + _target_short_size = config["DEPLOY"]["TARGET_SHORT_SIZE"].as(); + } + // 16.resize_type + if(config["DEPLOY"]["RESIZE_TYPE"].IsDefined() && + _scaling_map.find(config["DEPLOY"]["RESIZE_TYPE"].as()) != _scaling_map.end()) { + _resize_type = _scaling_map[config["DEPLOY"]["RESIZE_TYPE"].as()]; + } + else{ + _resize_type = 0; + } + // 17.resize_max_size + if(config["DEPLOY"]["RESIZE_MAX_SIZE"].IsDefined()) { + _resize_max_size = config["DEPLOY"]["RESIZE_MAX_SIZE"].as(); + } + // 18.feeds_size + if(config["DEPLOY"]["FEEDS_SIZE"].IsDefined()){ + _feeds_size = config["DEPLOY"]["FEEDS_SIZE"].as(); + } + // 19. coarsest_stride + if(config["DEPLOY"]["COARSEST_STRIDE"].IsDefined()) { + _coarsest_stride = config["DEPLOY"]["COARSEST_STRIDE"].as(); + } + return true; + } + + void debug() const { + + std::cout << "SCALE_RESIZE: (" << _resize[0] << ", " << _resize[1] << ")" << std::endl; + + std::cout << "MEAN: ["; + for (int i = 0; i < _mean.size(); ++i) { + if (i != _mean.size() - 1) { + std::cout << _mean[i] << ", "; + } else { + std::cout << _mean[i]; + } + } + std::cout << "]" << std::endl; + + std::cout << "STD: ["; + for (int i = 0; i < _std.size(); ++i) { + if (i != _std.size() - 1) { + std::cout << _std[i] << ", "; + } + else { + std::cout << _std[i]; + } + } + std::cout << "]" << std::endl; + std::cout << "DEPLOY.TARGET_SHORT_SIZE: " << _target_short_size << std::endl; + std::cout << "DEPLOY.IMAGE_TYPE: " << _img_type << std::endl; + std::cout << "DEPLOY.NUM_CLASSES: " << _class_num << std::endl; + std::cout << "DEPLOY.CHANNELS: " << _channels << std::endl; + std::cout << "DEPLOY.MODEL_PATH: " << _model_path << std::endl; + std::cout << "DEPLOY.MODEL_FILENAME: " << _model_file_name << std::endl; + std::cout << "DEPLOY.PARAMS_FILENAME: " << _param_file_name << std::endl; + std::cout << "DEPLOY.PRE_PROCESSOR: " << _pre_processor << std::endl; + std::cout << "DEPLOY.USE_GPU: " << _use_gpu << std::endl; + std::cout << "DEPLOY.PREDICTOR_MODE: " << _predictor_mode << std::endl; + std::cout << "DEPLOY.BATCH_SIZE: " << _batch_size << std::endl; + } + //DEPLOY.COARSEST_STRIDE + int _coarsest_stride; + // DEPLOY.FEEDS_SIZE + int _feeds_size; + // DEPLOY.RESIZE_TYPE 0:unpadding 1:rangescaling Default:0 + int _resize_type; + // DEPLOY.RESIZE_MAX_SIZE + int _resize_max_size; + // DEPLOY.CROP_SIZE + std::vector _crop_size; + // DEPLOY.SCALE_RESIZE + std::vector _resize; + // DEPLOY.MEAN + std::vector _mean; + // DEPLOY.STD + std::vector _std; + // DEPLOY.IMAGE_TYPE + std::string _img_type; + // DEPLOY.TARGET_SHORT_SIZE + int _target_short_size; + // DEPLOY.NUM_CLASSES + int _class_num; + // DEPLOY.CHANNELS + int _channels; + // DEPLOY.MODEL_PATH + std::string _model_path; + // DEPLOY.MODEL_FILENAME + std::string _model_file_name; + // DEPLOY.PARAMS_FILENAME + std::string _param_file_name; + // DEPLOY.PRE_PROCESSOR + std::string _pre_processor; + // DEPLOY.USE_GPU + int _use_gpu; + // DEPLOY.PREDICTOR_MODE + std::string _predictor_mode; + // DEPLOY.BATCH_SIZE + int _batch_size; + }; + +} diff --git a/inference/utils/detection_result.pb.cc b/inference/utils/detection_result.pb.cc new file mode 100644 index 0000000000000000000000000000000000000000..b5cce7317914cf93f99d0d4efa3aee763972cc4e --- /dev/null +++ b/inference/utils/detection_result.pb.cc @@ -0,0 +1,1159 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: detection_result.proto + +#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION +#include "detection_result.pb.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) + +namespace PaddleSolution { + +namespace { + +const ::google::protobuf::Descriptor* DetectionBox_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + DetectionBox_reflection_ = NULL; +const ::google::protobuf::Descriptor* DetectionResult_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + DetectionResult_reflection_ = NULL; + +} // namespace + + +void protobuf_AssignDesc_detection_5fresult_2eproto() GOOGLE_ATTRIBUTE_COLD; +void protobuf_AssignDesc_detection_5fresult_2eproto() { + protobuf_AddDesc_detection_5fresult_2eproto(); + const ::google::protobuf::FileDescriptor* file = + ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName( + "detection_result.proto"); + GOOGLE_CHECK(file != NULL); + DetectionBox_descriptor_ = file->message_type(0); + static const int DetectionBox_offsets_[6] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DetectionBox, class__), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DetectionBox, score_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DetectionBox, left_top_x_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DetectionBox, left_top_y_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DetectionBox, right_bottom_x_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DetectionBox, right_bottom_y_), + }; + DetectionBox_reflection_ = + ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection( + DetectionBox_descriptor_, + DetectionBox::internal_default_instance(), + DetectionBox_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DetectionBox, _has_bits_), + -1, + -1, + sizeof(DetectionBox), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DetectionBox, _internal_metadata_)); + DetectionResult_descriptor_ = file->message_type(1); + static const int DetectionResult_offsets_[2] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DetectionResult, filename_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DetectionResult, detection_boxes_), + }; + DetectionResult_reflection_ = + ::google::protobuf::internal::GeneratedMessageReflection::NewGeneratedMessageReflection( + DetectionResult_descriptor_, + DetectionResult::internal_default_instance(), + DetectionResult_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DetectionResult, _has_bits_), + -1, + -1, + sizeof(DetectionResult), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DetectionResult, _internal_metadata_)); +} + +namespace { + +GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_); +void protobuf_AssignDescriptorsOnce() { + ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_, + &protobuf_AssignDesc_detection_5fresult_2eproto); +} + +void protobuf_RegisterTypes(const ::std::string&) GOOGLE_ATTRIBUTE_COLD; +void protobuf_RegisterTypes(const ::std::string&) { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + DetectionBox_descriptor_, DetectionBox::internal_default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + DetectionResult_descriptor_, DetectionResult::internal_default_instance()); +} + +} // namespace + +void protobuf_ShutdownFile_detection_5fresult_2eproto() { + DetectionBox_default_instance_.Shutdown(); + delete DetectionBox_reflection_; + DetectionResult_default_instance_.Shutdown(); + delete DetectionResult_reflection_; +} + +void protobuf_InitDefaults_detection_5fresult_2eproto_impl() { + GOOGLE_PROTOBUF_VERIFY_VERSION; + + DetectionBox_default_instance_.DefaultConstruct(); + ::google::protobuf::internal::GetEmptyString(); + DetectionResult_default_instance_.DefaultConstruct(); + DetectionBox_default_instance_.get_mutable()->InitAsDefaultInstance(); + DetectionResult_default_instance_.get_mutable()->InitAsDefaultInstance(); +} + +GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_InitDefaults_detection_5fresult_2eproto_once_); +void protobuf_InitDefaults_detection_5fresult_2eproto() { + ::google::protobuf::GoogleOnceInit(&protobuf_InitDefaults_detection_5fresult_2eproto_once_, + &protobuf_InitDefaults_detection_5fresult_2eproto_impl); +} +void protobuf_AddDesc_detection_5fresult_2eproto_impl() { + GOOGLE_PROTOBUF_VERIFY_VERSION; + + protobuf_InitDefaults_detection_5fresult_2eproto(); + ::google::protobuf::DescriptorPool::InternalAddGeneratedFile( + "\n\026detection_result.proto\022\016PaddleSolution" + "\"\204\001\n\014DetectionBox\022\r\n\005class\030\001 \001(\005\022\r\n\005scor" + "e\030\002 \001(\002\022\022\n\nleft_top_x\030\003 \001(\002\022\022\n\nleft_top_" + "y\030\004 \001(\002\022\026\n\016right_bottom_x\030\005 \001(\002\022\026\n\016right" + "_bottom_y\030\006 \001(\002\"Z\n\017DetectionResult\022\020\n\010fi" + "lename\030\001 \001(\t\0225\n\017detection_boxes\030\002 \003(\0132\034." + "PaddleSolution.DetectionBox", 267); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( + "detection_result.proto", &protobuf_RegisterTypes); + ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_detection_5fresult_2eproto); +} + +GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AddDesc_detection_5fresult_2eproto_once_); +void protobuf_AddDesc_detection_5fresult_2eproto() { + ::google::protobuf::GoogleOnceInit(&protobuf_AddDesc_detection_5fresult_2eproto_once_, + &protobuf_AddDesc_detection_5fresult_2eproto_impl); +} +// Force AddDescriptors() to be called at static initialization time. +struct StaticDescriptorInitializer_detection_5fresult_2eproto { + StaticDescriptorInitializer_detection_5fresult_2eproto() { + protobuf_AddDesc_detection_5fresult_2eproto(); + } +} static_descriptor_initializer_detection_5fresult_2eproto_; + +namespace { + +static void MergeFromFail(int line) GOOGLE_ATTRIBUTE_COLD GOOGLE_ATTRIBUTE_NORETURN; +static void MergeFromFail(int line) { + ::google::protobuf::internal::MergeFromFail(__FILE__, line); +} + +} // namespace + + +// =================================================================== + +#if !defined(_MSC_VER) || _MSC_VER >= 1900 +const int DetectionBox::kClassFieldNumber; +const int DetectionBox::kScoreFieldNumber; +const int DetectionBox::kLeftTopXFieldNumber; +const int DetectionBox::kLeftTopYFieldNumber; +const int DetectionBox::kRightBottomXFieldNumber; +const int DetectionBox::kRightBottomYFieldNumber; +#endif // !defined(_MSC_VER) || _MSC_VER >= 1900 + +DetectionBox::DetectionBox() + : ::google::protobuf::Message(), _internal_metadata_(NULL) { + if (this != internal_default_instance()) protobuf_InitDefaults_detection_5fresult_2eproto(); + SharedCtor(); + // @@protoc_insertion_point(constructor:PaddleSolution.DetectionBox) +} + +void DetectionBox::InitAsDefaultInstance() { +} + +DetectionBox::DetectionBox(const DetectionBox& from) + : ::google::protobuf::Message(), + _internal_metadata_(NULL) { + SharedCtor(); + UnsafeMergeFrom(from); + // @@protoc_insertion_point(copy_constructor:PaddleSolution.DetectionBox) +} + +void DetectionBox::SharedCtor() { + _cached_size_ = 0; + ::memset(&class__, 0, reinterpret_cast(&right_bottom_y_) - + reinterpret_cast(&class__) + sizeof(right_bottom_y_)); +} + +DetectionBox::~DetectionBox() { + // @@protoc_insertion_point(destructor:PaddleSolution.DetectionBox) + SharedDtor(); +} + +void DetectionBox::SharedDtor() { +} + +void DetectionBox::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* DetectionBox::descriptor() { + protobuf_AssignDescriptorsOnce(); + return DetectionBox_descriptor_; +} + +const DetectionBox& DetectionBox::default_instance() { + protobuf_InitDefaults_detection_5fresult_2eproto(); + return *internal_default_instance(); +} + +::google::protobuf::internal::ExplicitlyConstructed DetectionBox_default_instance_; + +DetectionBox* DetectionBox::New(::google::protobuf::Arena* arena) const { + DetectionBox* n = new DetectionBox; + if (arena != NULL) { + arena->Own(n); + } + return n; +} + +void DetectionBox::Clear() { +// @@protoc_insertion_point(message_clear_start:PaddleSolution.DetectionBox) +#if defined(__clang__) +#define ZR_HELPER_(f) \ + _Pragma("clang diagnostic push") \ + _Pragma("clang diagnostic ignored \"-Winvalid-offsetof\"") \ + __builtin_offsetof(DetectionBox, f) \ + _Pragma("clang diagnostic pop") +#else +#define ZR_HELPER_(f) reinterpret_cast(\ + &reinterpret_cast(16)->f) +#endif + +#define ZR_(first, last) do {\ + ::memset(&(first), 0,\ + ZR_HELPER_(last) - ZR_HELPER_(first) + sizeof(last));\ +} while (0) + + ZR_(class__, right_bottom_y_); + +#undef ZR_HELPER_ +#undef ZR_ + + _has_bits_.Clear(); + if (_internal_metadata_.have_unknown_fields()) { + mutable_unknown_fields()->Clear(); + } +} + +bool DetectionBox::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure + ::google::protobuf::uint32 tag; + // @@protoc_insertion_point(parse_start:PaddleSolution.DetectionBox) + for (;;) { + ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); + tag = p.first; + if (!p.second) goto handle_unusual; + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional int32 class = 1; + case 1: { + if (tag == 8) { + set_has_class_(); + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( + input, &class__))); + } else { + goto handle_unusual; + } + if (input->ExpectTag(21)) goto parse_score; + break; + } + + // optional float score = 2; + case 2: { + if (tag == 21) { + parse_score: + set_has_score(); + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>( + input, &score_))); + } else { + goto handle_unusual; + } + if (input->ExpectTag(29)) goto parse_left_top_x; + break; + } + + // optional float left_top_x = 3; + case 3: { + if (tag == 29) { + parse_left_top_x: + set_has_left_top_x(); + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>( + input, &left_top_x_))); + } else { + goto handle_unusual; + } + if (input->ExpectTag(37)) goto parse_left_top_y; + break; + } + + // optional float left_top_y = 4; + case 4: { + if (tag == 37) { + parse_left_top_y: + set_has_left_top_y(); + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>( + input, &left_top_y_))); + } else { + goto handle_unusual; + } + if (input->ExpectTag(45)) goto parse_right_bottom_x; + break; + } + + // optional float right_bottom_x = 5; + case 5: { + if (tag == 45) { + parse_right_bottom_x: + set_has_right_bottom_x(); + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>( + input, &right_bottom_x_))); + } else { + goto handle_unusual; + } + if (input->ExpectTag(53)) goto parse_right_bottom_y; + break; + } + + // optional float right_bottom_y = 6; + case 6: { + if (tag == 53) { + parse_right_bottom_y: + set_has_right_bottom_y(); + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>( + input, &right_bottom_y_))); + } else { + goto handle_unusual; + } + if (input->ExpectAtEnd()) goto success; + break; + } + + default: { + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + goto success; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } +success: + // @@protoc_insertion_point(parse_success:PaddleSolution.DetectionBox) + return true; +failure: + // @@protoc_insertion_point(parse_failure:PaddleSolution.DetectionBox) + return false; +#undef DO_ +} + +void DetectionBox::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:PaddleSolution.DetectionBox) + // optional int32 class = 1; + if (has_class_()) { + ::google::protobuf::internal::WireFormatLite::WriteInt32(1, this->class_(), output); + } + + // optional float score = 2; + if (has_score()) { + ::google::protobuf::internal::WireFormatLite::WriteFloat(2, this->score(), output); + } + + // optional float left_top_x = 3; + if (has_left_top_x()) { + ::google::protobuf::internal::WireFormatLite::WriteFloat(3, this->left_top_x(), output); + } + + // optional float left_top_y = 4; + if (has_left_top_y()) { + ::google::protobuf::internal::WireFormatLite::WriteFloat(4, this->left_top_y(), output); + } + + // optional float right_bottom_x = 5; + if (has_right_bottom_x()) { + ::google::protobuf::internal::WireFormatLite::WriteFloat(5, this->right_bottom_x(), output); + } + + // optional float right_bottom_y = 6; + if (has_right_bottom_y()) { + ::google::protobuf::internal::WireFormatLite::WriteFloat(6, this->right_bottom_y(), output); + } + + if (_internal_metadata_.have_unknown_fields()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } + // @@protoc_insertion_point(serialize_end:PaddleSolution.DetectionBox) +} + +::google::protobuf::uint8* DetectionBox::InternalSerializeWithCachedSizesToArray( + bool deterministic, ::google::protobuf::uint8* target) const { + (void)deterministic; // Unused + // @@protoc_insertion_point(serialize_to_array_start:PaddleSolution.DetectionBox) + // optional int32 class = 1; + if (has_class_()) { + target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(1, this->class_(), target); + } + + // optional float score = 2; + if (has_score()) { + target = ::google::protobuf::internal::WireFormatLite::WriteFloatToArray(2, this->score(), target); + } + + // optional float left_top_x = 3; + if (has_left_top_x()) { + target = ::google::protobuf::internal::WireFormatLite::WriteFloatToArray(3, this->left_top_x(), target); + } + + // optional float left_top_y = 4; + if (has_left_top_y()) { + target = ::google::protobuf::internal::WireFormatLite::WriteFloatToArray(4, this->left_top_y(), target); + } + + // optional float right_bottom_x = 5; + if (has_right_bottom_x()) { + target = ::google::protobuf::internal::WireFormatLite::WriteFloatToArray(5, this->right_bottom_x(), target); + } + + // optional float right_bottom_y = 6; + if (has_right_bottom_y()) { + target = ::google::protobuf::internal::WireFormatLite::WriteFloatToArray(6, this->right_bottom_y(), target); + } + + if (_internal_metadata_.have_unknown_fields()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + // @@protoc_insertion_point(serialize_to_array_end:PaddleSolution.DetectionBox) + return target; +} + +size_t DetectionBox::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:PaddleSolution.DetectionBox) + size_t total_size = 0; + + if (_has_bits_[0 / 32] & 63u) { + // optional int32 class = 1; + if (has_class_()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::Int32Size( + this->class_()); + } + + // optional float score = 2; + if (has_score()) { + total_size += 1 + 4; + } + + // optional float left_top_x = 3; + if (has_left_top_x()) { + total_size += 1 + 4; + } + + // optional float left_top_y = 4; + if (has_left_top_y()) { + total_size += 1 + 4; + } + + // optional float right_bottom_x = 5; + if (has_right_bottom_x()) { + total_size += 1 + 4; + } + + // optional float right_bottom_y = 6; + if (has_right_bottom_y()) { + total_size += 1 + 4; + } + + } + if (_internal_metadata_.have_unknown_fields()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + int cached_size = ::google::protobuf::internal::ToCachedSize(total_size); + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = cached_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void DetectionBox::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:PaddleSolution.DetectionBox) + if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); + const DetectionBox* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); + if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:PaddleSolution.DetectionBox) + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:PaddleSolution.DetectionBox) + UnsafeMergeFrom(*source); + } +} + +void DetectionBox::MergeFrom(const DetectionBox& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:PaddleSolution.DetectionBox) + if (GOOGLE_PREDICT_TRUE(&from != this)) { + UnsafeMergeFrom(from); + } else { + MergeFromFail(__LINE__); + } +} + +void DetectionBox::UnsafeMergeFrom(const DetectionBox& from) { + GOOGLE_DCHECK(&from != this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_class_()) { + set_class_(from.class_()); + } + if (from.has_score()) { + set_score(from.score()); + } + if (from.has_left_top_x()) { + set_left_top_x(from.left_top_x()); + } + if (from.has_left_top_y()) { + set_left_top_y(from.left_top_y()); + } + if (from.has_right_bottom_x()) { + set_right_bottom_x(from.right_bottom_x()); + } + if (from.has_right_bottom_y()) { + set_right_bottom_y(from.right_bottom_y()); + } + } + if (from._internal_metadata_.have_unknown_fields()) { + ::google::protobuf::UnknownFieldSet::MergeToInternalMetdata( + from.unknown_fields(), &_internal_metadata_); + } +} + +void DetectionBox::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:PaddleSolution.DetectionBox) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void DetectionBox::CopyFrom(const DetectionBox& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:PaddleSolution.DetectionBox) + if (&from == this) return; + Clear(); + UnsafeMergeFrom(from); +} + +bool DetectionBox::IsInitialized() const { + + return true; +} + +void DetectionBox::Swap(DetectionBox* other) { + if (other == this) return; + InternalSwap(other); +} +void DetectionBox::InternalSwap(DetectionBox* other) { + std::swap(class__, other->class__); + std::swap(score_, other->score_); + std::swap(left_top_x_, other->left_top_x_); + std::swap(left_top_y_, other->left_top_y_); + std::swap(right_bottom_x_, other->right_bottom_x_); + std::swap(right_bottom_y_, other->right_bottom_y_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _internal_metadata_.Swap(&other->_internal_metadata_); + std::swap(_cached_size_, other->_cached_size_); +} + +::google::protobuf::Metadata DetectionBox::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = DetectionBox_descriptor_; + metadata.reflection = DetectionBox_reflection_; + return metadata; +} + +#if PROTOBUF_INLINE_NOT_IN_HEADERS +// DetectionBox + +// optional int32 class = 1; +bool DetectionBox::has_class_() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +void DetectionBox::set_has_class_() { + _has_bits_[0] |= 0x00000001u; +} +void DetectionBox::clear_has_class_() { + _has_bits_[0] &= ~0x00000001u; +} +void DetectionBox::clear_class_() { + class__ = 0; + clear_has_class_(); +} +::google::protobuf::int32 DetectionBox::class_() const { + // @@protoc_insertion_point(field_get:PaddleSolution.DetectionBox.class) + return class__; +} +void DetectionBox::set_class_(::google::protobuf::int32 value) { + set_has_class_(); + class__ = value; + // @@protoc_insertion_point(field_set:PaddleSolution.DetectionBox.class) +} + +// optional float score = 2; +bool DetectionBox::has_score() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +void DetectionBox::set_has_score() { + _has_bits_[0] |= 0x00000002u; +} +void DetectionBox::clear_has_score() { + _has_bits_[0] &= ~0x00000002u; +} +void DetectionBox::clear_score() { + score_ = 0; + clear_has_score(); +} +float DetectionBox::score() const { + // @@protoc_insertion_point(field_get:PaddleSolution.DetectionBox.score) + return score_; +} +void DetectionBox::set_score(float value) { + set_has_score(); + score_ = value; + // @@protoc_insertion_point(field_set:PaddleSolution.DetectionBox.score) +} + +// optional float left_top_x = 3; +bool DetectionBox::has_left_top_x() const { + return (_has_bits_[0] & 0x00000004u) != 0; +} +void DetectionBox::set_has_left_top_x() { + _has_bits_[0] |= 0x00000004u; +} +void DetectionBox::clear_has_left_top_x() { + _has_bits_[0] &= ~0x00000004u; +} +void DetectionBox::clear_left_top_x() { + left_top_x_ = 0; + clear_has_left_top_x(); +} +float DetectionBox::left_top_x() const { + // @@protoc_insertion_point(field_get:PaddleSolution.DetectionBox.left_top_x) + return left_top_x_; +} +void DetectionBox::set_left_top_x(float value) { + set_has_left_top_x(); + left_top_x_ = value; + // @@protoc_insertion_point(field_set:PaddleSolution.DetectionBox.left_top_x) +} + +// optional float left_top_y = 4; +bool DetectionBox::has_left_top_y() const { + return (_has_bits_[0] & 0x00000008u) != 0; +} +void DetectionBox::set_has_left_top_y() { + _has_bits_[0] |= 0x00000008u; +} +void DetectionBox::clear_has_left_top_y() { + _has_bits_[0] &= ~0x00000008u; +} +void DetectionBox::clear_left_top_y() { + left_top_y_ = 0; + clear_has_left_top_y(); +} +float DetectionBox::left_top_y() const { + // @@protoc_insertion_point(field_get:PaddleSolution.DetectionBox.left_top_y) + return left_top_y_; +} +void DetectionBox::set_left_top_y(float value) { + set_has_left_top_y(); + left_top_y_ = value; + // @@protoc_insertion_point(field_set:PaddleSolution.DetectionBox.left_top_y) +} + +// optional float right_bottom_x = 5; +bool DetectionBox::has_right_bottom_x() const { + return (_has_bits_[0] & 0x00000010u) != 0; +} +void DetectionBox::set_has_right_bottom_x() { + _has_bits_[0] |= 0x00000010u; +} +void DetectionBox::clear_has_right_bottom_x() { + _has_bits_[0] &= ~0x00000010u; +} +void DetectionBox::clear_right_bottom_x() { + right_bottom_x_ = 0; + clear_has_right_bottom_x(); +} +float DetectionBox::right_bottom_x() const { + // @@protoc_insertion_point(field_get:PaddleSolution.DetectionBox.right_bottom_x) + return right_bottom_x_; +} +void DetectionBox::set_right_bottom_x(float value) { + set_has_right_bottom_x(); + right_bottom_x_ = value; + // @@protoc_insertion_point(field_set:PaddleSolution.DetectionBox.right_bottom_x) +} + +// optional float right_bottom_y = 6; +bool DetectionBox::has_right_bottom_y() const { + return (_has_bits_[0] & 0x00000020u) != 0; +} +void DetectionBox::set_has_right_bottom_y() { + _has_bits_[0] |= 0x00000020u; +} +void DetectionBox::clear_has_right_bottom_y() { + _has_bits_[0] &= ~0x00000020u; +} +void DetectionBox::clear_right_bottom_y() { + right_bottom_y_ = 0; + clear_has_right_bottom_y(); +} +float DetectionBox::right_bottom_y() const { + // @@protoc_insertion_point(field_get:PaddleSolution.DetectionBox.right_bottom_y) + return right_bottom_y_; +} +void DetectionBox::set_right_bottom_y(float value) { + set_has_right_bottom_y(); + right_bottom_y_ = value; + // @@protoc_insertion_point(field_set:PaddleSolution.DetectionBox.right_bottom_y) +} + +inline const DetectionBox* DetectionBox::internal_default_instance() { + return &DetectionBox_default_instance_.get(); +} +#endif // PROTOBUF_INLINE_NOT_IN_HEADERS + +// =================================================================== + +#if !defined(_MSC_VER) || _MSC_VER >= 1900 +const int DetectionResult::kFilenameFieldNumber; +const int DetectionResult::kDetectionBoxesFieldNumber; +#endif // !defined(_MSC_VER) || _MSC_VER >= 1900 + +DetectionResult::DetectionResult() + : ::google::protobuf::Message(), _internal_metadata_(NULL) { + if (this != internal_default_instance()) protobuf_InitDefaults_detection_5fresult_2eproto(); + SharedCtor(); + // @@protoc_insertion_point(constructor:PaddleSolution.DetectionResult) +} + +void DetectionResult::InitAsDefaultInstance() { +} + +DetectionResult::DetectionResult(const DetectionResult& from) + : ::google::protobuf::Message(), + _internal_metadata_(NULL) { + SharedCtor(); + UnsafeMergeFrom(from); + // @@protoc_insertion_point(copy_constructor:PaddleSolution.DetectionResult) +} + +void DetectionResult::SharedCtor() { + _cached_size_ = 0; + filename_.UnsafeSetDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + +DetectionResult::~DetectionResult() { + // @@protoc_insertion_point(destructor:PaddleSolution.DetectionResult) + SharedDtor(); +} + +void DetectionResult::SharedDtor() { + filename_.DestroyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} + +void DetectionResult::SetCachedSize(int size) const { + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); +} +const ::google::protobuf::Descriptor* DetectionResult::descriptor() { + protobuf_AssignDescriptorsOnce(); + return DetectionResult_descriptor_; +} + +const DetectionResult& DetectionResult::default_instance() { + protobuf_InitDefaults_detection_5fresult_2eproto(); + return *internal_default_instance(); +} + +::google::protobuf::internal::ExplicitlyConstructed DetectionResult_default_instance_; + +DetectionResult* DetectionResult::New(::google::protobuf::Arena* arena) const { + DetectionResult* n = new DetectionResult; + if (arena != NULL) { + arena->Own(n); + } + return n; +} + +void DetectionResult::Clear() { +// @@protoc_insertion_point(message_clear_start:PaddleSolution.DetectionResult) + if (has_filename()) { + filename_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + } + detection_boxes_.Clear(); + _has_bits_.Clear(); + if (_internal_metadata_.have_unknown_fields()) { + mutable_unknown_fields()->Clear(); + } +} + +bool DetectionResult::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!GOOGLE_PREDICT_TRUE(EXPRESSION)) goto failure + ::google::protobuf::uint32 tag; + // @@protoc_insertion_point(parse_start:PaddleSolution.DetectionResult) + for (;;) { + ::std::pair< ::google::protobuf::uint32, bool> p = input->ReadTagWithCutoff(127); + tag = p.first; + if (!p.second) goto handle_unusual; + switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) { + // optional string filename = 1; + case 1: { + if (tag == 10) { + DO_(::google::protobuf::internal::WireFormatLite::ReadString( + input, this->mutable_filename())); + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( + this->filename().data(), this->filename().length(), + ::google::protobuf::internal::WireFormat::PARSE, + "PaddleSolution.DetectionResult.filename"); + } else { + goto handle_unusual; + } + if (input->ExpectTag(18)) goto parse_detection_boxes; + break; + } + + // repeated .PaddleSolution.DetectionBox detection_boxes = 2; + case 2: { + if (tag == 18) { + parse_detection_boxes: + DO_(input->IncrementRecursionDepth()); + parse_loop_detection_boxes: + DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtualNoRecursionDepth( + input, add_detection_boxes())); + } else { + goto handle_unusual; + } + if (input->ExpectTag(18)) goto parse_loop_detection_boxes; + input->UnsafeDecrementRecursionDepth(); + if (input->ExpectAtEnd()) goto success; + break; + } + + default: { + handle_unusual: + if (tag == 0 || + ::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) { + goto success; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } +success: + // @@protoc_insertion_point(parse_success:PaddleSolution.DetectionResult) + return true; +failure: + // @@protoc_insertion_point(parse_failure:PaddleSolution.DetectionResult) + return false; +#undef DO_ +} + +void DetectionResult::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { + // @@protoc_insertion_point(serialize_start:PaddleSolution.DetectionResult) + // optional string filename = 1; + if (has_filename()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( + this->filename().data(), this->filename().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE, + "PaddleSolution.DetectionResult.filename"); + ::google::protobuf::internal::WireFormatLite::WriteStringMaybeAliased( + 1, this->filename(), output); + } + + // repeated .PaddleSolution.DetectionBox detection_boxes = 2; + for (unsigned int i = 0, n = this->detection_boxes_size(); i < n; i++) { + ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray( + 2, this->detection_boxes(i), output); + } + + if (_internal_metadata_.have_unknown_fields()) { + ::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output); + } + // @@protoc_insertion_point(serialize_end:PaddleSolution.DetectionResult) +} + +::google::protobuf::uint8* DetectionResult::InternalSerializeWithCachedSizesToArray( + bool deterministic, ::google::protobuf::uint8* target) const { + (void)deterministic; // Unused + // @@protoc_insertion_point(serialize_to_array_start:PaddleSolution.DetectionResult) + // optional string filename = 1; + if (has_filename()) { + ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField( + this->filename().data(), this->filename().length(), + ::google::protobuf::internal::WireFormat::SERIALIZE, + "PaddleSolution.DetectionResult.filename"); + target = + ::google::protobuf::internal::WireFormatLite::WriteStringToArray( + 1, this->filename(), target); + } + + // repeated .PaddleSolution.DetectionBox detection_boxes = 2; + for (unsigned int i = 0, n = this->detection_boxes_size(); i < n; i++) { + target = ::google::protobuf::internal::WireFormatLite:: + InternalWriteMessageNoVirtualToArray( + 2, this->detection_boxes(i), false, target); + } + + if (_internal_metadata_.have_unknown_fields()) { + target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray( + unknown_fields(), target); + } + // @@protoc_insertion_point(serialize_to_array_end:PaddleSolution.DetectionResult) + return target; +} + +size_t DetectionResult::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:PaddleSolution.DetectionResult) + size_t total_size = 0; + + // optional string filename = 1; + if (has_filename()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::StringSize( + this->filename()); + } + + // repeated .PaddleSolution.DetectionBox detection_boxes = 2; + { + unsigned int count = this->detection_boxes_size(); + total_size += 1UL * count; + for (unsigned int i = 0; i < count; i++) { + total_size += + ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual( + this->detection_boxes(i)); + } + } + + if (_internal_metadata_.have_unknown_fields()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + int cached_size = ::google::protobuf::internal::ToCachedSize(total_size); + GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN(); + _cached_size_ = cached_size; + GOOGLE_SAFE_CONCURRENT_WRITES_END(); + return total_size; +} + +void DetectionResult::MergeFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:PaddleSolution.DetectionResult) + if (GOOGLE_PREDICT_FALSE(&from == this)) MergeFromFail(__LINE__); + const DetectionResult* source = + ::google::protobuf::internal::DynamicCastToGenerated( + &from); + if (source == NULL) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:PaddleSolution.DetectionResult) + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:PaddleSolution.DetectionResult) + UnsafeMergeFrom(*source); + } +} + +void DetectionResult::MergeFrom(const DetectionResult& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:PaddleSolution.DetectionResult) + if (GOOGLE_PREDICT_TRUE(&from != this)) { + UnsafeMergeFrom(from); + } else { + MergeFromFail(__LINE__); + } +} + +void DetectionResult::UnsafeMergeFrom(const DetectionResult& from) { + GOOGLE_DCHECK(&from != this); + detection_boxes_.MergeFrom(from.detection_boxes_); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from.has_filename()) { + set_has_filename(); + filename_.AssignWithDefault(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), from.filename_); + } + } + if (from._internal_metadata_.have_unknown_fields()) { + ::google::protobuf::UnknownFieldSet::MergeToInternalMetdata( + from.unknown_fields(), &_internal_metadata_); + } +} + +void DetectionResult::CopyFrom(const ::google::protobuf::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:PaddleSolution.DetectionResult) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void DetectionResult::CopyFrom(const DetectionResult& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:PaddleSolution.DetectionResult) + if (&from == this) return; + Clear(); + UnsafeMergeFrom(from); +} + +bool DetectionResult::IsInitialized() const { + + return true; +} + +void DetectionResult::Swap(DetectionResult* other) { + if (other == this) return; + InternalSwap(other); +} +void DetectionResult::InternalSwap(DetectionResult* other) { + filename_.Swap(&other->filename_); + detection_boxes_.UnsafeArenaSwap(&other->detection_boxes_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _internal_metadata_.Swap(&other->_internal_metadata_); + std::swap(_cached_size_, other->_cached_size_); +} + +::google::protobuf::Metadata DetectionResult::GetMetadata() const { + protobuf_AssignDescriptorsOnce(); + ::google::protobuf::Metadata metadata; + metadata.descriptor = DetectionResult_descriptor_; + metadata.reflection = DetectionResult_reflection_; + return metadata; +} + +#if PROTOBUF_INLINE_NOT_IN_HEADERS +// DetectionResult + +// optional string filename = 1; +bool DetectionResult::has_filename() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +void DetectionResult::set_has_filename() { + _has_bits_[0] |= 0x00000001u; +} +void DetectionResult::clear_has_filename() { + _has_bits_[0] &= ~0x00000001u; +} +void DetectionResult::clear_filename() { + filename_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + clear_has_filename(); +} +const ::std::string& DetectionResult::filename() const { + // @@protoc_insertion_point(field_get:PaddleSolution.DetectionResult.filename) + return filename_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +void DetectionResult::set_filename(const ::std::string& value) { + set_has_filename(); + filename_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:PaddleSolution.DetectionResult.filename) +} +void DetectionResult::set_filename(const char* value) { + set_has_filename(); + filename_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:PaddleSolution.DetectionResult.filename) +} +void DetectionResult::set_filename(const char* value, size_t size) { + set_has_filename(); + filename_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:PaddleSolution.DetectionResult.filename) +} +::std::string* DetectionResult::mutable_filename() { + set_has_filename(); + // @@protoc_insertion_point(field_mutable:PaddleSolution.DetectionResult.filename) + return filename_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +::std::string* DetectionResult::release_filename() { + // @@protoc_insertion_point(field_release:PaddleSolution.DetectionResult.filename) + clear_has_filename(); + return filename_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +void DetectionResult::set_allocated_filename(::std::string* filename) { + if (filename != NULL) { + set_has_filename(); + } else { + clear_has_filename(); + } + filename_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), filename); + // @@protoc_insertion_point(field_set_allocated:PaddleSolution.DetectionResult.filename) +} + +// repeated .PaddleSolution.DetectionBox detection_boxes = 2; +int DetectionResult::detection_boxes_size() const { + return detection_boxes_.size(); +} +void DetectionResult::clear_detection_boxes() { + detection_boxes_.Clear(); +} +const ::PaddleSolution::DetectionBox& DetectionResult::detection_boxes(int index) const { + // @@protoc_insertion_point(field_get:PaddleSolution.DetectionResult.detection_boxes) + return detection_boxes_.Get(index); +} +::PaddleSolution::DetectionBox* DetectionResult::mutable_detection_boxes(int index) { + // @@protoc_insertion_point(field_mutable:PaddleSolution.DetectionResult.detection_boxes) + return detection_boxes_.Mutable(index); +} +::PaddleSolution::DetectionBox* DetectionResult::add_detection_boxes() { + // @@protoc_insertion_point(field_add:PaddleSolution.DetectionResult.detection_boxes) + return detection_boxes_.Add(); +} +::google::protobuf::RepeatedPtrField< ::PaddleSolution::DetectionBox >* +DetectionResult::mutable_detection_boxes() { + // @@protoc_insertion_point(field_mutable_list:PaddleSolution.DetectionResult.detection_boxes) + return &detection_boxes_; +} +const ::google::protobuf::RepeatedPtrField< ::PaddleSolution::DetectionBox >& +DetectionResult::detection_boxes() const { + // @@protoc_insertion_point(field_list:PaddleSolution.DetectionResult.detection_boxes) + return detection_boxes_; +} + +inline const DetectionResult* DetectionResult::internal_default_instance() { + return &DetectionResult_default_instance_.get(); +} +#endif // PROTOBUF_INLINE_NOT_IN_HEADERS + +// @@protoc_insertion_point(namespace_scope) + +} // namespace PaddleSolution + +// @@protoc_insertion_point(global_scope) diff --git a/inference/utils/detection_result.pb.h b/inference/utils/detection_result.pb.h new file mode 100644 index 0000000000000000000000000000000000000000..1b2f89ea9ca13f3f949bd19b097bb514a4afc525 --- /dev/null +++ b/inference/utils/detection_result.pb.h @@ -0,0 +1,563 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: detection_result.proto + +#ifndef PROTOBUF_detection_5fresult_2eproto__INCLUDED +#define PROTOBUF_detection_5fresult_2eproto__INCLUDED + +#include + +#include + +#if GOOGLE_PROTOBUF_VERSION < 3001000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 3001000 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) + +namespace PaddleSolution { + +// Internal implementation detail -- do not call these. +void protobuf_AddDesc_detection_5fresult_2eproto(); +void protobuf_InitDefaults_detection_5fresult_2eproto(); +void protobuf_AssignDesc_detection_5fresult_2eproto(); +void protobuf_ShutdownFile_detection_5fresult_2eproto(); + +class DetectionBox; +class DetectionResult; + +// =================================================================== + +class DetectionBox : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:PaddleSolution.DetectionBox) */ { + public: + DetectionBox(); + virtual ~DetectionBox(); + + DetectionBox(const DetectionBox& from); + + inline DetectionBox& operator=(const DetectionBox& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _internal_metadata_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _internal_metadata_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const DetectionBox& default_instance(); + + static const DetectionBox* internal_default_instance(); + + void Swap(DetectionBox* other); + + // implements Message ---------------------------------------------- + + inline DetectionBox* New() const { return New(NULL); } + + DetectionBox* New(::google::protobuf::Arena* arena) const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const DetectionBox& from); + void MergeFrom(const DetectionBox& from); + void Clear(); + bool IsInitialized() const; + + size_t ByteSizeLong() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray( + bool deterministic, ::google::protobuf::uint8* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const { + return InternalSerializeWithCachedSizesToArray(false, output); + } + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + void InternalSwap(DetectionBox* other); + void UnsafeMergeFrom(const DetectionBox& from); + private: + inline ::google::protobuf::Arena* GetArenaNoVirtual() const { + return _internal_metadata_.arena(); + } + inline void* MaybeArenaPtr() const { + return _internal_metadata_.raw_arena_ptr(); + } + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional int32 class = 1; + bool has_class_() const; + void clear_class_(); + static const int kClassFieldNumber = 1; + ::google::protobuf::int32 class_() const; + void set_class_(::google::protobuf::int32 value); + + // optional float score = 2; + bool has_score() const; + void clear_score(); + static const int kScoreFieldNumber = 2; + float score() const; + void set_score(float value); + + // optional float left_top_x = 3; + bool has_left_top_x() const; + void clear_left_top_x(); + static const int kLeftTopXFieldNumber = 3; + float left_top_x() const; + void set_left_top_x(float value); + + // optional float left_top_y = 4; + bool has_left_top_y() const; + void clear_left_top_y(); + static const int kLeftTopYFieldNumber = 4; + float left_top_y() const; + void set_left_top_y(float value); + + // optional float right_bottom_x = 5; + bool has_right_bottom_x() const; + void clear_right_bottom_x(); + static const int kRightBottomXFieldNumber = 5; + float right_bottom_x() const; + void set_right_bottom_x(float value); + + // optional float right_bottom_y = 6; + bool has_right_bottom_y() const; + void clear_right_bottom_y(); + static const int kRightBottomYFieldNumber = 6; + float right_bottom_y() const; + void set_right_bottom_y(float value); + + // @@protoc_insertion_point(class_scope:PaddleSolution.DetectionBox) + private: + inline void set_has_class_(); + inline void clear_has_class_(); + inline void set_has_score(); + inline void clear_has_score(); + inline void set_has_left_top_x(); + inline void clear_has_left_top_x(); + inline void set_has_left_top_y(); + inline void clear_has_left_top_y(); + inline void set_has_right_bottom_x(); + inline void clear_has_right_bottom_x(); + inline void set_has_right_bottom_y(); + inline void clear_has_right_bottom_y(); + + ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + ::google::protobuf::internal::HasBits<1> _has_bits_; + mutable int _cached_size_; + ::google::protobuf::int32 class__; + float score_; + float left_top_x_; + float left_top_y_; + float right_bottom_x_; + float right_bottom_y_; + friend void protobuf_InitDefaults_detection_5fresult_2eproto_impl(); + friend void protobuf_AddDesc_detection_5fresult_2eproto_impl(); + friend void protobuf_AssignDesc_detection_5fresult_2eproto(); + friend void protobuf_ShutdownFile_detection_5fresult_2eproto(); + + void InitAsDefaultInstance(); +}; +extern ::google::protobuf::internal::ExplicitlyConstructed DetectionBox_default_instance_; + +// ------------------------------------------------------------------- + +class DetectionResult : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:PaddleSolution.DetectionResult) */ { + public: + DetectionResult(); + virtual ~DetectionResult(); + + DetectionResult(const DetectionResult& from); + + inline DetectionResult& operator=(const DetectionResult& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _internal_metadata_.unknown_fields(); + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return _internal_metadata_.mutable_unknown_fields(); + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const DetectionResult& default_instance(); + + static const DetectionResult* internal_default_instance(); + + void Swap(DetectionResult* other); + + // implements Message ---------------------------------------------- + + inline DetectionResult* New() const { return New(NULL); } + + DetectionResult* New(::google::protobuf::Arena* arena) const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const DetectionResult& from); + void MergeFrom(const DetectionResult& from); + void Clear(); + bool IsInitialized() const; + + size_t ByteSizeLong() const; + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + void SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + ::google::protobuf::uint8* InternalSerializeWithCachedSizesToArray( + bool deterministic, ::google::protobuf::uint8* output) const; + ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const { + return InternalSerializeWithCachedSizesToArray(false, output); + } + int GetCachedSize() const { return _cached_size_; } + private: + void SharedCtor(); + void SharedDtor(); + void SetCachedSize(int size) const; + void InternalSwap(DetectionResult* other); + void UnsafeMergeFrom(const DetectionResult& from); + private: + inline ::google::protobuf::Arena* GetArenaNoVirtual() const { + return _internal_metadata_.arena(); + } + inline void* MaybeArenaPtr() const { + return _internal_metadata_.raw_arena_ptr(); + } + public: + + ::google::protobuf::Metadata GetMetadata() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional string filename = 1; + bool has_filename() const; + void clear_filename(); + static const int kFilenameFieldNumber = 1; + const ::std::string& filename() const; + void set_filename(const ::std::string& value); + void set_filename(const char* value); + void set_filename(const char* value, size_t size); + ::std::string* mutable_filename(); + ::std::string* release_filename(); + void set_allocated_filename(::std::string* filename); + + // repeated .PaddleSolution.DetectionBox detection_boxes = 2; + int detection_boxes_size() const; + void clear_detection_boxes(); + static const int kDetectionBoxesFieldNumber = 2; + const ::PaddleSolution::DetectionBox& detection_boxes(int index) const; + ::PaddleSolution::DetectionBox* mutable_detection_boxes(int index); + ::PaddleSolution::DetectionBox* add_detection_boxes(); + ::google::protobuf::RepeatedPtrField< ::PaddleSolution::DetectionBox >* + mutable_detection_boxes(); + const ::google::protobuf::RepeatedPtrField< ::PaddleSolution::DetectionBox >& + detection_boxes() const; + + // @@protoc_insertion_point(class_scope:PaddleSolution.DetectionResult) + private: + inline void set_has_filename(); + inline void clear_has_filename(); + + ::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_; + ::google::protobuf::internal::HasBits<1> _has_bits_; + mutable int _cached_size_; + ::google::protobuf::RepeatedPtrField< ::PaddleSolution::DetectionBox > detection_boxes_; + ::google::protobuf::internal::ArenaStringPtr filename_; + friend void protobuf_InitDefaults_detection_5fresult_2eproto_impl(); + friend void protobuf_AddDesc_detection_5fresult_2eproto_impl(); + friend void protobuf_AssignDesc_detection_5fresult_2eproto(); + friend void protobuf_ShutdownFile_detection_5fresult_2eproto(); + + void InitAsDefaultInstance(); +}; +extern ::google::protobuf::internal::ExplicitlyConstructed DetectionResult_default_instance_; + +// =================================================================== + + +// =================================================================== + +#if !PROTOBUF_INLINE_NOT_IN_HEADERS +// DetectionBox + +// optional int32 class = 1; +inline bool DetectionBox::has_class_() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void DetectionBox::set_has_class_() { + _has_bits_[0] |= 0x00000001u; +} +inline void DetectionBox::clear_has_class_() { + _has_bits_[0] &= ~0x00000001u; +} +inline void DetectionBox::clear_class_() { + class__ = 0; + clear_has_class_(); +} +inline ::google::protobuf::int32 DetectionBox::class_() const { + // @@protoc_insertion_point(field_get:PaddleSolution.DetectionBox.class) + return class__; +} +inline void DetectionBox::set_class_(::google::protobuf::int32 value) { + set_has_class_(); + class__ = value; + // @@protoc_insertion_point(field_set:PaddleSolution.DetectionBox.class) +} + +// optional float score = 2; +inline bool DetectionBox::has_score() const { + return (_has_bits_[0] & 0x00000002u) != 0; +} +inline void DetectionBox::set_has_score() { + _has_bits_[0] |= 0x00000002u; +} +inline void DetectionBox::clear_has_score() { + _has_bits_[0] &= ~0x00000002u; +} +inline void DetectionBox::clear_score() { + score_ = 0; + clear_has_score(); +} +inline float DetectionBox::score() const { + // @@protoc_insertion_point(field_get:PaddleSolution.DetectionBox.score) + return score_; +} +inline void DetectionBox::set_score(float value) { + set_has_score(); + score_ = value; + // @@protoc_insertion_point(field_set:PaddleSolution.DetectionBox.score) +} + +// optional float left_top_x = 3; +inline bool DetectionBox::has_left_top_x() const { + return (_has_bits_[0] & 0x00000004u) != 0; +} +inline void DetectionBox::set_has_left_top_x() { + _has_bits_[0] |= 0x00000004u; +} +inline void DetectionBox::clear_has_left_top_x() { + _has_bits_[0] &= ~0x00000004u; +} +inline void DetectionBox::clear_left_top_x() { + left_top_x_ = 0; + clear_has_left_top_x(); +} +inline float DetectionBox::left_top_x() const { + // @@protoc_insertion_point(field_get:PaddleSolution.DetectionBox.left_top_x) + return left_top_x_; +} +inline void DetectionBox::set_left_top_x(float value) { + set_has_left_top_x(); + left_top_x_ = value; + // @@protoc_insertion_point(field_set:PaddleSolution.DetectionBox.left_top_x) +} + +// optional float left_top_y = 4; +inline bool DetectionBox::has_left_top_y() const { + return (_has_bits_[0] & 0x00000008u) != 0; +} +inline void DetectionBox::set_has_left_top_y() { + _has_bits_[0] |= 0x00000008u; +} +inline void DetectionBox::clear_has_left_top_y() { + _has_bits_[0] &= ~0x00000008u; +} +inline void DetectionBox::clear_left_top_y() { + left_top_y_ = 0; + clear_has_left_top_y(); +} +inline float DetectionBox::left_top_y() const { + // @@protoc_insertion_point(field_get:PaddleSolution.DetectionBox.left_top_y) + return left_top_y_; +} +inline void DetectionBox::set_left_top_y(float value) { + set_has_left_top_y(); + left_top_y_ = value; + // @@protoc_insertion_point(field_set:PaddleSolution.DetectionBox.left_top_y) +} + +// optional float right_bottom_x = 5; +inline bool DetectionBox::has_right_bottom_x() const { + return (_has_bits_[0] & 0x00000010u) != 0; +} +inline void DetectionBox::set_has_right_bottom_x() { + _has_bits_[0] |= 0x00000010u; +} +inline void DetectionBox::clear_has_right_bottom_x() { + _has_bits_[0] &= ~0x00000010u; +} +inline void DetectionBox::clear_right_bottom_x() { + right_bottom_x_ = 0; + clear_has_right_bottom_x(); +} +inline float DetectionBox::right_bottom_x() const { + // @@protoc_insertion_point(field_get:PaddleSolution.DetectionBox.right_bottom_x) + return right_bottom_x_; +} +inline void DetectionBox::set_right_bottom_x(float value) { + set_has_right_bottom_x(); + right_bottom_x_ = value; + // @@protoc_insertion_point(field_set:PaddleSolution.DetectionBox.right_bottom_x) +} + +// optional float right_bottom_y = 6; +inline bool DetectionBox::has_right_bottom_y() const { + return (_has_bits_[0] & 0x00000020u) != 0; +} +inline void DetectionBox::set_has_right_bottom_y() { + _has_bits_[0] |= 0x00000020u; +} +inline void DetectionBox::clear_has_right_bottom_y() { + _has_bits_[0] &= ~0x00000020u; +} +inline void DetectionBox::clear_right_bottom_y() { + right_bottom_y_ = 0; + clear_has_right_bottom_y(); +} +inline float DetectionBox::right_bottom_y() const { + // @@protoc_insertion_point(field_get:PaddleSolution.DetectionBox.right_bottom_y) + return right_bottom_y_; +} +inline void DetectionBox::set_right_bottom_y(float value) { + set_has_right_bottom_y(); + right_bottom_y_ = value; + // @@protoc_insertion_point(field_set:PaddleSolution.DetectionBox.right_bottom_y) +} + +inline const DetectionBox* DetectionBox::internal_default_instance() { + return &DetectionBox_default_instance_.get(); +} +// ------------------------------------------------------------------- + +// DetectionResult + +// optional string filename = 1; +inline bool DetectionResult::has_filename() const { + return (_has_bits_[0] & 0x00000001u) != 0; +} +inline void DetectionResult::set_has_filename() { + _has_bits_[0] |= 0x00000001u; +} +inline void DetectionResult::clear_has_filename() { + _has_bits_[0] &= ~0x00000001u; +} +inline void DetectionResult::clear_filename() { + filename_.ClearToEmptyNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); + clear_has_filename(); +} +inline const ::std::string& DetectionResult::filename() const { + // @@protoc_insertion_point(field_get:PaddleSolution.DetectionResult.filename) + return filename_.GetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void DetectionResult::set_filename(const ::std::string& value) { + set_has_filename(); + filename_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), value); + // @@protoc_insertion_point(field_set:PaddleSolution.DetectionResult.filename) +} +inline void DetectionResult::set_filename(const char* value) { + set_has_filename(); + filename_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), ::std::string(value)); + // @@protoc_insertion_point(field_set_char:PaddleSolution.DetectionResult.filename) +} +inline void DetectionResult::set_filename(const char* value, size_t size) { + set_has_filename(); + filename_.SetNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), + ::std::string(reinterpret_cast(value), size)); + // @@protoc_insertion_point(field_set_pointer:PaddleSolution.DetectionResult.filename) +} +inline ::std::string* DetectionResult::mutable_filename() { + set_has_filename(); + // @@protoc_insertion_point(field_mutable:PaddleSolution.DetectionResult.filename) + return filename_.MutableNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline ::std::string* DetectionResult::release_filename() { + // @@protoc_insertion_point(field_release:PaddleSolution.DetectionResult.filename) + clear_has_filename(); + return filename_.ReleaseNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited()); +} +inline void DetectionResult::set_allocated_filename(::std::string* filename) { + if (filename != NULL) { + set_has_filename(); + } else { + clear_has_filename(); + } + filename_.SetAllocatedNoArena(&::google::protobuf::internal::GetEmptyStringAlreadyInited(), filename); + // @@protoc_insertion_point(field_set_allocated:PaddleSolution.DetectionResult.filename) +} + +// repeated .PaddleSolution.DetectionBox detection_boxes = 2; +inline int DetectionResult::detection_boxes_size() const { + return detection_boxes_.size(); +} +inline void DetectionResult::clear_detection_boxes() { + detection_boxes_.Clear(); +} +inline const ::PaddleSolution::DetectionBox& DetectionResult::detection_boxes(int index) const { + // @@protoc_insertion_point(field_get:PaddleSolution.DetectionResult.detection_boxes) + return detection_boxes_.Get(index); +} +inline ::PaddleSolution::DetectionBox* DetectionResult::mutable_detection_boxes(int index) { + // @@protoc_insertion_point(field_mutable:PaddleSolution.DetectionResult.detection_boxes) + return detection_boxes_.Mutable(index); +} +inline ::PaddleSolution::DetectionBox* DetectionResult::add_detection_boxes() { + // @@protoc_insertion_point(field_add:PaddleSolution.DetectionResult.detection_boxes) + return detection_boxes_.Add(); +} +inline ::google::protobuf::RepeatedPtrField< ::PaddleSolution::DetectionBox >* +DetectionResult::mutable_detection_boxes() { + // @@protoc_insertion_point(field_mutable_list:PaddleSolution.DetectionResult.detection_boxes) + return &detection_boxes_; +} +inline const ::google::protobuf::RepeatedPtrField< ::PaddleSolution::DetectionBox >& +DetectionResult::detection_boxes() const { + // @@protoc_insertion_point(field_list:PaddleSolution.DetectionResult.detection_boxes) + return detection_boxes_; +} + +inline const DetectionResult* DetectionResult::internal_default_instance() { + return &DetectionResult_default_instance_.get(); +} +#endif // !PROTOBUF_INLINE_NOT_IN_HEADERS +// ------------------------------------------------------------------- + + +// @@protoc_insertion_point(namespace_scope) + +} // namespace PaddleSolution + +// @@protoc_insertion_point(global_scope) + +#endif // PROTOBUF_detection_5fresult_2eproto__INCLUDED diff --git a/inference/utils/detection_result.proto b/inference/utils/detection_result.proto new file mode 100644 index 0000000000000000000000000000000000000000..2d1cbb2464ac09b0dcea01f8331da5ee7894a4d5 --- /dev/null +++ b/inference/utils/detection_result.proto @@ -0,0 +1,21 @@ +syntax = "proto2"; +package PaddleSolution; + +message DetectionBox { + optional int32 class = 1; + optional float score = 2; + optional float left_top_x = 3; + optional float left_top_y = 4; + optional float right_bottom_x = 5; + optional float right_bottom_y = 6; +} + +message DetectionResult { + optional string filename = 1; + repeated DetectionBox detection_boxes = 2; +} + +//message DetectionResultsContainer { +// repeated DetectionResult result = 1; +//} + diff --git a/inference/utils/utils.h b/inference/utils/utils.h new file mode 100644 index 0000000000000000000000000000000000000000..63245219edb6ad39e896f1eb041e8bff69613382 --- /dev/null +++ b/inference/utils/utils.h @@ -0,0 +1,124 @@ +// 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. + +#pragma once + +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +namespace PaddleSolution { + namespace utils { + enum SCALE_TYPE{ + UNPADDING, + RANGE_SCALING + }; + inline std::string path_join(const std::string& dir, const std::string& path) { + std::string seperator = "/"; + #ifdef _WIN32 + seperator = "\\"; + #endif + return dir + seperator + path; + } + #ifndef _WIN32 + // scan a directory and get all files with input extensions + inline std::vector get_directory_images(const std::string& path, const std::string& exts) + { + std::vector imgs; + struct dirent *entry; + DIR *dir = opendir(path.c_str()); + if (dir == NULL) { + closedir(dir); + return imgs; + } + + while ((entry = readdir(dir)) != NULL) { + std::string item = entry->d_name; + auto ext = strrchr(entry->d_name, '.'); + if (!ext || std::string(ext) == "." || std::string(ext) == "..") { + continue; + } + if (exts.find(ext) != std::string::npos) { + imgs.push_back(path_join(path, entry->d_name)); + } + } + sort(imgs.begin(), imgs.end()); + return imgs; + } + #else + // scan a directory and get all files with input extensions + inline std::vector get_directory_images(const std::string& path, const std::string& exts) + { + std::vector imgs; + for (const auto& item : std::experimental::filesystem::directory_iterator(path)) { + auto suffix = item.path().extension().string(); + if (exts.find(suffix) != std::string::npos && suffix.size() > 0) { + auto fullname = path_join(path, item.path().filename().string()); + imgs.push_back(item.path().string()); + } + } + sort(imgs.begin(), imgs.end()); + return imgs; + } + #endif + + inline int scaling(int resize_type, int &w, int &h, int new_w, int new_h, int target_size, int max_size, float &im_scale_ratio) + { + if(w <= 0 || h <= 0 || new_w <= 0 || new_h <= 0){ + return -1; + } + switch(resize_type) { + case SCALE_TYPE::UNPADDING: + { + w = new_w; + h = new_h; + im_scale_ratio=0; + } + break; + case SCALE_TYPE::RANGE_SCALING: + { + int im_max_size = std::max(w, h); + int im_min_size = std::min(w, h); + float scale_ratio= static_cast(target_size) / static_cast(im_min_size); + if(max_size > 0) { + if(round(scale_ratio * im_max_size) > max_size) { + scale_ratio = static_cast(max_size) / static_cast(im_max_size); + } + } + w = round(scale_ratio * static_cast(w)); + h = round(scale_ratio * static_cast(h)); + im_scale_ratio = scale_ratio; + } + break; + default : + { + std::cout << "Can't support this type of scaling strategy." << std::endl; + std::cout << "Throw exception at file " << __FILE__ << " on line " << __LINE__ << std::endl; + throw 0; + } + break; + } + return 0; + } + } +} diff --git a/ppdet/__init__.py b/ppdet/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d0c32e26092f6ea25771279418582a24ea449ab2 --- /dev/null +++ b/ppdet/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/ppdet/core/__init__.py b/ppdet/core/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f8561f944e4ca7453456c476092ba4e2d7a0bb5d --- /dev/null +++ b/ppdet/core/__init__.py @@ -0,0 +1,17 @@ +# 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 ppdet.modeling +import ppdet.optimizer +import ppdet.data diff --git a/ppdet/core/config/__init__.py b/ppdet/core/config/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d0c32e26092f6ea25771279418582a24ea449ab2 --- /dev/null +++ b/ppdet/core/config/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/ppdet/core/config/schema.py b/ppdet/core/config/schema.py new file mode 100644 index 0000000000000000000000000000000000000000..efba5be1dfd93243ff7a071f0f30accfa513e18f --- /dev/null +++ b/ppdet/core/config/schema.py @@ -0,0 +1,258 @@ +# 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 inspect +import importlib +import re + +try: + from docstring_parser import parse as doc_parse +except Exception: + + def doc_parse(*args): + if not doc_parse.__warning_sent__: + from ppdet.utils.cli import ColorTTY + color_tty = ColorTTY() + message = "docstring_parser is not installed, " \ + + "argument description is not available" + print(color_tty.yellow(message)) + doc_parse.__warning_sent__ = True + + doc_parse.__warning_sent__ = False + +try: + from typeguard import check_type +except Exception: + + def check_type(*args): + if not check_type.__warning_sent__: + from ppdet.utils.cli import ColorTTY + color_tty = ColorTTY() + message = "typeguard is not installed," \ + + "type checking is not available" + print(color_tty.yellow(message)) + check_type.__warning_sent__ = True + + check_type.__warning_sent__ = False + +__all__ = ['SchemaValue', 'SchemaDict', 'SharedConfig', 'extract_schema'] + + +class SchemaValue(object): + def __init__(self, name, doc='', type=None): + super(SchemaValue, self).__init__() + self.name = name + self.doc = doc + self.type = type + + def set_default(self, value): + self.default = value + + def has_default(self): + return hasattr(self, 'default') + + +class SchemaDict(dict): + def __init__(self, **kwargs): + super(SchemaDict, self).__init__() + self.schema = {} + self.strict = False + self.doc = "" + self.update(kwargs) + + def __setitem__(self, key, value): + # XXX also update regular dict to SchemaDict?? + if isinstance(value, dict) and key in self and isinstance(self[key], + SchemaDict): + self[key].update(value) + else: + super(SchemaDict, self).__setitem__(key, value) + + def __missing__(self, key): + if self.has_default(key): + return self.schema[key].default + elif key in self.schema: + return self.schema[key] + else: + raise KeyError(key) + + def copy(self): + newone = SchemaDict() + newone.__dict__.update(self.__dict__) + newone.update(self) + return newone + + def set_schema(self, key, value): + assert isinstance(value, SchemaValue) + self.schema[key] = value + + def set_strict(self, strict): + self.strict = strict + + def has_default(self, key): + return key in self.schema and self.schema[key].has_default() + + def is_default(self, key): + if not self.has_default(key): + return False + if hasattr(self[key], '__dict__'): + return True + else: + return key not in self or self[key] == self.schema[key].default + + def find_default_keys(self): + return [ + k for k in list(self.keys()) + list(self.schema.keys()) + if self.is_default(k) + ] + + def mandatory(self): + return any([k for k in self.schema.keys() if not self.has_default(k)]) + + def find_missing_keys(self): + missing = [ + k for k in self.schema.keys() + if k not in self and not self.has_default(k) + ] + placeholders = [k for k in self if self[k] in ('', '')] + return missing + placeholders + + def find_extra_keys(self): + return list(set(self.keys()) - set(self.schema.keys())) + + def find_mismatch_keys(self): + mismatch_keys = [] + for arg in self.schema.values(): + if arg.type is not None: + try: + check_type("{}.{}".format(self.name, arg.name), + self[arg.name], arg.type) + except Exception: + mismatch_keys.append(arg.name) + return mismatch_keys + + def validate(self): + missing_keys = self.find_missing_keys() + if missing_keys: + raise ValueError("Missing param for class<{}>: {}".format( + self.name, ", ".join(missing_keys))) + extra_keys = self.find_extra_keys() + if extra_keys and self.strict: + raise ValueError("Extraneous param for class<{}>: {}".format( + self.name, ", ".join(extra_keys))) + mismatch_keys = self.find_mismatch_keys() + if mismatch_keys: + raise TypeError("Wrong param type for class<{}>: {}".format( + self.name, ", ".join(mismatch_keys))) + + +class SharedConfig(object): + """ + Representation class for `__shared__` annotations, which work as follows: + + - if `key` is set for the module in config file, its value will take + precedence + - if `key` is not set for the module but present in the config file, its + value will be used + - otherwise, use the provided `default_value` as fallback + + Args: + key: config[key] will be injected + default_value: fallback value + """ + + def __init__(self, key, default_value=None): + super(SharedConfig, self).__init__() + self.key = key + self.default_value = default_value + + +def extract_schema(cls): + """ + Extract schema from a given class + + Args: + cls (type): Class from which to extract. + + Returns: + schema (SchemaDict): Extracted schema. + """ + ctor = cls.__init__ + # python 2 compatibility + if hasattr(inspect, 'getfullargspec'): + argspec = inspect.getfullargspec(ctor) + annotations = argspec.annotations + has_kwargs = argspec.varkw is not None + else: + argspec = inspect.getargspec(ctor) + # python 2 type hinting workaround, see pep-3107 + # however, since `typeguard` does not support python 2, type checking + # is still python 3 only for now + annotations = getattr(ctor, '__annotations__', {}) + has_kwargs = argspec.keywords is not None + + names = [arg for arg in argspec.args if arg != 'self'] + defaults = argspec.defaults + num_defaults = argspec.defaults is not None and len(argspec.defaults) or 0 + num_required = len(names) - num_defaults + + docs = cls.__doc__ + if docs is None and getattr(cls, '__category__', None) == 'op': + docs = cls.__call__.__doc__ + docstring = doc_parse(docs) + if docstring is None: + comments = {} + else: + comments = {} + for p in docstring.params: + match_obj = re.match('^([a-zA-Z_]+[a-zA-Z_0-9]*).*', p.arg_name) + if match_obj is not None: + comments[match_obj.group(1)] = p.description + + schema = SchemaDict() + schema.name = cls.__name__ + schema.doc = "" + if docs is not None: + start_pos = docs[0] == '\n' and 1 or 0 + schema.doc = docs[start_pos:].split("\n")[0].strip() + # XXX handle paddle's weird doc convention + if '**' == schema.doc[:2] and '**' == schema.doc[-2:]: + schema.doc = schema.doc[2:-2].strip() + schema.category = hasattr(cls, '__category__') and getattr( + cls, '__category__') or 'module' + schema.strict = not has_kwargs + schema.pymodule = importlib.import_module(cls.__module__) + schema.inject = getattr(cls, '__inject__', []) + schema.shared = getattr(cls, '__shared__', []) + for idx, name in enumerate(names): + comment = name in comments and comments[name] or name + if name in schema.inject: + type_ = None + else: + type_ = name in annotations and annotations[name] or None + value_schema = SchemaValue(name, comment, type_) + if name in schema.shared: + assert idx >= num_required, "shared config must have default value" + default = defaults[idx - num_required] + value_schema.set_default(SharedConfig(name, default)) + elif idx >= num_required: + default = defaults[idx - num_required] + value_schema.set_default(default) + schema.set_schema(name, value_schema) + + return schema diff --git a/ppdet/core/config/yaml_helpers.py b/ppdet/core/config/yaml_helpers.py new file mode 100644 index 0000000000000000000000000000000000000000..8a7738b47f4f86acde78ab8a3bcac590d61615fa --- /dev/null +++ b/ppdet/core/config/yaml_helpers.py @@ -0,0 +1,109 @@ +# 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 importlib +import inspect + +import yaml +from .schema import SharedConfig + +__all__ = ['serializable', 'Callable'] + + +def _make_python_constructor(cls): + def python_constructor(loader, node): + if isinstance(node, yaml.SequenceNode): + args = loader.construct_sequence(node, deep=True) + return cls(*args) + else: + kwargs = loader.construct_mapping(node, deep=True) + try: + return cls(**kwargs) + except Exception as ex: + print("Error when construct {} instance from yaml config". + format(cls.__name__)) + raise ex + + return python_constructor + + +def _make_python_representer(cls): + # python 2 compatibility + if hasattr(inspect, 'getfullargspec'): + argspec = inspect.getfullargspec(cls) + else: + argspec = inspect.getargspec(cls.__init__) + argnames = [arg for arg in argspec.args if arg != 'self'] + + def python_representer(dumper, obj): + if argnames: + data = {name: getattr(obj, name) for name in argnames} + else: + data = obj.__dict__ + if '_id' in data: + del data['_id'] + return dumper.represent_mapping(u'!{}'.format(cls.__name__), data) + + return python_representer + + +def serializable(cls): + """ + Add loader and dumper for given class, which must be + "trivially serializable" + + Args: + cls: class to be serialized + + Returns: cls + """ + yaml.add_constructor(u'!{}'.format(cls.__name__), + _make_python_constructor(cls)) + yaml.add_representer(cls, _make_python_representer(cls)) + return cls + + +yaml.add_representer(SharedConfig, + lambda d, o: d.represent_data(o.default_value)) + + +@serializable +class Callable(object): + """ + Helper to be used in Yaml for creating arbitrary class objects + + Args: + full_type (str): the full module path to target function + """ + + def __init__(self, full_type, args=[], kwargs={}): + super(Callable, self).__init__() + self.full_type = full_type + self.args = args + self.kwargs = kwargs + + def __call__(self): + if '.' in self.full_type: + idx = self.full_type.rfind('.') + module = importlib.import_module(self.full_type[:idx]) + func_name = self.full_type[idx + 1:] + else: + try: + module = importlib.import_module('builtins') + except Exception: + module = importlib.import_module('__builtin__') + func_name = self.full_type + + func = getattr(module, func_name) + return func(*self.args, **self.kwargs) diff --git a/ppdet/core/workspace.py b/ppdet/core/workspace.py new file mode 100644 index 0000000000000000000000000000000000000000..bf505d6e4d1aab311057763c52f4ef501606a7fb --- /dev/null +++ b/ppdet/core/workspace.py @@ -0,0 +1,207 @@ +# 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 importlib +import os +import sys + +import yaml +import copy + +from .config.schema import SchemaDict, SharedConfig, extract_schema +from .config.yaml_helpers import serializable + +__all__ = [ + 'global_config', + 'load_config', + 'merge_config', + 'get_registered_modules', + 'create', + 'register', + 'serializable', + 'dump_value', +] + + +def dump_value(value): + # XXX this is hackish, but collections.abc is not available in python 2 + if hasattr(value, '__dict__') or isinstance(value, (dict, tuple, list)): + value = yaml.dump(value, default_flow_style=True) + value = value.replace('\n', '') + value = value.replace('...', '') + return "'{}'".format(value) + else: + # primitive types + return str(value) + + +class AttrDict(dict): + """Single level attribute dict, NOT recursive""" + + def __init__(self, **kwargs): + super(AttrDict, self).__init__() + super(AttrDict, self).update(kwargs) + + def __getattr__(self, key): + if key in self: + return self[key] + raise AttributeError("object has no attribute '{}'".format(key)) + + +global_config = AttrDict() + + +def load_config(file_path): + """ + Load config from file. + + Args: + file_path (str): Path of the config file to be loaded. + + Returns: global config + """ + _, ext = os.path.splitext(file_path) + assert ext in ['.yml', '.yaml'], "only support yaml files for now" + with open(file_path) as f: + merge_config(yaml.load(f, Loader=yaml.Loader)) + return global_config + + +def merge_config(config): + """ + Merge config into global config. + + 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 + + +def get_registered_modules(): + return {k: v for k, v in global_config.items() if isinstance(v, SchemaDict)} + + +def make_partial(cls): + op_module = importlib.import_module(cls.__op__.__module__) + op = getattr(op_module, cls.__op__.__name__) + cls.__category__ = getattr(cls, '__category__', None) or 'op' + + def partial_apply(self, *args, **kwargs): + kwargs_ = self.__dict__.copy() + kwargs_.update(kwargs) + return op(*args, **kwargs_) + + if getattr(cls, '__append_doc__', True): # XXX should default to True? + if sys.version_info[0] > 2: + cls.__doc__ = "Wrapper for `{}` OP".format(op.__name__) + cls.__init__.__doc__ = op.__doc__ + cls.__call__ = partial_apply + cls.__call__.__doc__ = op.__doc__ + else: + # XXX work around for python 2 + partial_apply.__doc__ = op.__doc__ + cls.__call__ = partial_apply + return cls + + +def register(cls): + """ + Register a given module class. + + Args: + cls (type): Module class to be registered. + + Returns: cls + """ + if cls.__name__ in global_config: + raise ValueError("Module class already registered: {}".format( + cls.__name__)) + if hasattr(cls, '__op__'): + cls = make_partial(cls) + global_config[cls.__name__] = extract_schema(cls) + return cls + + +def create(cls_or_name, **kwargs): + """ + Create an instance of given module class. + + Args: + cls_or_name (type or str): Class of which to create instance. + + Returns: instance of type `cls_or_name` + """ + assert type(cls_or_name) in [type, str + ], "should be a class or name of a class" + name = type(cls_or_name) == str and cls_or_name or cls_or_name.__name__ + assert name in global_config and \ + isinstance(global_config[name], SchemaDict), \ + "the module {} is not registered".format(name) + config = global_config[name] + config.update(kwargs) + config.validate() + cls = getattr(config.pymodule, name) + + kwargs = {} + kwargs.update(global_config[name]) + + # parse `shared` annoation of registered modules + if getattr(config, 'shared', None): + for k in config.shared: + target_key = config[k] + shared_conf = config.schema[k].default + assert isinstance(shared_conf, SharedConfig) + if target_key is not None and not isinstance(target_key, + SharedConfig): + continue # value is given for the module + elif shared_conf.key in global_config: + # `key` is present in config + kwargs[k] = global_config[shared_conf.key] + else: + kwargs[k] = shared_conf.default_value + + # parse `inject` annoation of registered modules + if getattr(config, 'inject', None): + for k in config.inject: + target_key = config[k] + # optional dependency + if target_key is None: + continue + # also accept dictionaries and serialized objects + if isinstance(target_key, dict) or hasattr(target_key, '__dict__'): + continue + elif isinstance(target_key, str): + if target_key not in global_config: + raise ValueError("Missing injection config:", target_key) + target = global_config[target_key] + if isinstance(target, SchemaDict): + kwargs[k] = create(target_key) + elif hasattr(target, '__dict__'): # serialized object + kwargs[k] = target + else: + raise ValueError("Unsupported injection type:", target_key) + # prevent modification of global config values of reference types + # (e.g., list, dict) from within the created module instances + kwargs = copy.deepcopy(kwargs) + return cls(**kwargs) diff --git a/ppdet/data/README.md b/ppdet/data/README.md new file mode 120000 index 0000000000000000000000000000000000000000..238fc99bf487f0505c27541ecaa9a64b0bcd62f7 --- /dev/null +++ b/ppdet/data/README.md @@ -0,0 +1 @@ +docs/DATA.md \ No newline at end of file diff --git a/ppdet/data/README_cn.md b/ppdet/data/README_cn.md new file mode 120000 index 0000000000000000000000000000000000000000..c8e59f3054954c6abe6732b01998a87d6d3074c4 --- /dev/null +++ b/ppdet/data/README_cn.md @@ -0,0 +1 @@ +docs/DATA_cn.md \ No newline at end of file diff --git a/ppdet/data/__init__.py b/ppdet/data/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1104c33f6ac34b8ec32681f5c4a7fc4d89274bfb --- /dev/null +++ b/ppdet/data/__init__.py @@ -0,0 +1,46 @@ +# 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: +# 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'] diff --git a/ppdet/data/data_feed.py b/ppdet/data/data_feed.py new file mode 100644 index 0000000000000000000000000000000000000000..c384b2cb3241cc5c012cedc02dfc9cbeab524bf6 --- /dev/null +++ b/ppdet/data/data_feed.py @@ -0,0 +1,1067 @@ +# 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) +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=[3, 800, 1333], + 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=[3, 800, 1333], + 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=[3, 800, 1333], + 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), + 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=[3, 800, 1333], + 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=[3, 800, 1333], + 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=[3, 800, 1333], + 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), + 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), + NormalizeBox(), + RandomDistort(), + ExpandImage(max_ratio=4., prob=.5, + mean=[123.675, 116.28, 103.53]), + CropImage([[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), + ], + 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) + + +@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) +# yapf: enable diff --git a/ppdet/data/dataset.py b/ppdet/data/dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..31d4df4a0146fd7bc5cdd21d02e06d58ebe0f99f --- /dev/null +++ b/ppdet/data/dataset.py @@ -0,0 +1,63 @@ +# 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 + + def __next__(self): + return self.next() + + def __iter__(self): + return self + + def __str__(self): + return "{}(fname:{}, epoch:{:d}, size:{:d}, pos:{:d})".format( + type(self).__name__, self._fname, 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/reader.py b/ppdet/data/reader.py new file mode 100644 index 0000000000000000000000000000000000000000..b2d4d07850601fb047b7b17fcb7c33ce6f35f6ea --- /dev/null +++ b/ppdet/data/reader.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. + +# 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 logging +from .source import build_source +from .transform import build_mapper, map, batch, batch_map + +logger = logging.getLogger(__name__) + + +class Reader(object): + """Interface to make readers for training or evaluation""" + + 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" + + 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] + + # 1, Build data source + + sc_conf = {'data_cf': file_conf, 'cname2cid': self._cname2cid} + sc = build_source(sc_conf) + 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: + yield _batch + n += 1 + if maxit > 0 and n == maxit: + return + batched_ds.reset() + if maxit <= 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 diff --git a/ppdet/data/source/__init__.py b/ppdet/data/source/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e55df6962b36906edac71e8b3cb25334a1d336a4 --- /dev/null +++ b/ppdet/data/source/__init__.py @@ -0,0 +1,73 @@ +# 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 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) diff --git a/ppdet/data/source/class_aware_sampling_roidb_source.py b/ppdet/data/source/class_aware_sampling_roidb_source.py new file mode 100644 index 0000000000000000000000000000000000000000..0175037c352594c48cce09ca033de18534937f87 --- /dev/null +++ b/ppdet/data/source/class_aware_sampling_roidb_source.py @@ -0,0 +1,132 @@ +# 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_loader.py b/ppdet/data/source/coco_loader.py new file mode 100644 index 0000000000000000000000000000000000000000..db18498905358eef66b07969dab8f65606d3cdc0 --- /dev/null +++ b/ppdet/data/source/coco_loader.py @@ -0,0 +1,128 @@ +# 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/iterator_source.py b/ppdet/data/source/iterator_source.py new file mode 100644 index 0000000000000000000000000000000000000000..2785d4843e660843b554197a82530c8129244321 --- /dev/null +++ b/ppdet/data/source/iterator_source.py @@ -0,0 +1,103 @@ +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..bc73c8ea67b3bd82ca38ba4a65fd7a185a0c86b1 --- /dev/null +++ b/ppdet/data/source/loader.py @@ -0,0 +1,143 @@ +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..7d42e87b898010235f794ffdc3d6b3e4ee2adc24 --- /dev/null +++ b/ppdet/data/source/roidb_source.py @@ -0,0 +1,170 @@ +# 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 + + +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(fname:%s,epoch:%d,size:%d,pos:%d)' \ + % (self._fname, self._epoch, self.size(), self._pos) + + 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._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 new file mode 100644 index 0000000000000000000000000000000000000000..a65dd054e369d97b303b9018facac3381440dfec --- /dev/null +++ b/ppdet/data/source/simple_source.py @@ -0,0 +1,113 @@ +# 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_loader.py b/ppdet/data/source/voc_loader.py new file mode 100644 index 0000000000000000000000000000000000000000..8fc1b79547b8e70b612e956c9c0ac3b1427f5b19 --- /dev/null +++ b/ppdet/data/source/voc_loader.py @@ -0,0 +1,272 @@ +# 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_loader.py b/ppdet/data/source/widerface_loader.py new file mode 100644 index 0000000000000000000000000000000000000000..97ed476004e8c7a6ef67dfe155487efd2277f15c --- /dev/null +++ b/ppdet/data/source/widerface_loader.py @@ -0,0 +1,128 @@ +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..b829107b842f6f15706744fdcbea05ec7341b311 Binary files /dev/null and b/ppdet/data/tests/000012.jpg differ diff --git a/ppdet/data/tests/coco.yml b/ppdet/data/tests/coco.yml new file mode 100644 index 0000000000000000000000000000000000000000..80ae7ed9e424601082e233e7158d9c0a28f1727b --- /dev/null +++ b/ppdet/data/tests/coco.yml @@ -0,0 +1,48 @@ +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 new file mode 100755 index 0000000000000000000000000000000000000000..a81abc5d80b16e795d8020d00a27ef7c851f0185 --- /dev/null +++ b/ppdet/data/tests/data/prepare_data.sh @@ -0,0 +1,43 @@ +#!/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 new file mode 100644 index 0000000000000000000000000000000000000000..b57fd55e94df68a4a9ddbbaa60ca20226377c451 --- /dev/null +++ b/ppdet/data/tests/rcnn_dataset.yml @@ -0,0 +1,32 @@ +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 new file mode 100644 index 0000000000000000000000000000000000000000..a1882d5dd38a4da14335244e7603af47568b7bbf --- /dev/null +++ b/ppdet/data/tests/run_all_tests.py @@ -0,0 +1,45 @@ +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..bc46ac0f10e3b1690cf59bd6803eedd0bee6d9d4 --- /dev/null +++ b/ppdet/data/tests/set_env.py @@ -0,0 +1,51 @@ +# 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_iterator_source.py b/ppdet/data/tests/test_iterator_source.py new file mode 100644 index 0000000000000000000000000000000000000000..c32a734fb258dce2205d24f1dc60479fadaf57a6 --- /dev/null +++ b/ppdet/data/tests/test_iterator_source.py @@ -0,0 +1,73 @@ +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..dc835aa0f12ecb82e267c57f7a9f875a9adc48ce --- /dev/null +++ b/ppdet/data/tests/test_loader.py @@ -0,0 +1,108 @@ +# 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 +from __future__ import unicode_literals +import os +import time +import unittest +import sys +import logging +import numpy as np + +import set_env + + +class TestLoader(unittest.TestCase): + """Test cases for dataset.source.loader + """ + + @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") + + @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) + + +if __name__ == '__main__': + unittest.main() diff --git a/ppdet/data/tests/test_operator.py b/ppdet/data/tests/test_operator.py new file mode 100644 index 0000000000000000000000000000000000000000..85d5b229d48fab968f38639d5f7533e5646f25c3 --- /dev/null +++ b/ppdet/data/tests/test_operator.py @@ -0,0 +1,156 @@ +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..e94484dff077d9b12a82f7337cd9cb165e8bd23f --- /dev/null +++ b/ppdet/data/tests/test_reader.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 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 new file mode 100644 index 0000000000000000000000000000000000000000..105cc9cd7d9d971bf2a3f69cccf251ce2943275a --- /dev/null +++ b/ppdet/data/tests/test_roidb_source.py @@ -0,0 +1,74 @@ +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..9123669293e6d0f432d1e90aa32e933d7fb6851c --- /dev/null +++ b/ppdet/data/tests/test_transformer.py @@ -0,0 +1,117 @@ +# 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 new file mode 100644 index 0000000000000000000000000000000000000000..30b196f61418f28edbdb80c95a1d9adbdf00c11b --- /dev/null +++ b/ppdet/data/tools/generate_data_for_training.py @@ -0,0 +1,147 @@ +# 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/tools/x2coco.py b/ppdet/data/tools/x2coco.py new file mode 100644 index 0000000000000000000000000000000000000000..da8e4aef4011ef1a23e7459bc473301e171b9fea --- /dev/null +++ b/ppdet/data/tools/x2coco.py @@ -0,0 +1,294 @@ +#!/usr/bin/env python +# coding: utf-8 +# 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 argparse +import glob +import json +import os +import os.path as osp +import sys +import shutil + +import numpy as np +import PIL.ImageDraw + + +class MyEncoder(json.JSONEncoder): + def default(self, obj): + if isinstance(obj, np.integer): + return int(obj) + elif isinstance(obj, np.floating): + return float(obj) + elif isinstance(obj, np.ndarray): + return obj.tolist() + else: + return super(MyEncoder, self).default(obj) + + +def getbbox(self, points): + polygons = points + mask = self.polygons_to_mask([self.height, self.width], polygons) + return self.mask2box(mask) + + +def images_labelme(data, num): + image = {} + image['height'] = data['imageHeight'] + image['width'] = data['imageWidth'] + image['id'] = num + 1 + image['file_name'] = data['imagePath'].split('/')[-1] + return image + +def images_cityscape(data, num, img_file): + image = {} + image['height'] = data['imgHeight'] + image['width'] = data['imgWidth'] + image['id'] = num + 1 + image['file_name'] = img_file + return image + + +def categories(label, labels_list): + category = {} + category['supercategory'] = 'component' + category['id'] = len(labels_list) + 1 + category['name'] = label + return category + + +def annotations_rectangle(points, label, image_num, object_num, label_to_num): + annotation = {} + seg_points = np.asarray(points).copy() + seg_points[1, :] = np.asarray(points)[2, :] + seg_points[2, :] = np.asarray(points)[1, :] + annotation['segmentation'] = [list(seg_points.flatten())] + annotation['iscrowd'] = 0 + annotation['image_id'] = image_num + 1 + annotation['bbox'] = list( + map(float, [ + points[0][0], points[0][1], points[1][0] - points[0][0], points[1][ + 1] - points[0][1] + ])) + annotation['area'] = annotation['bbox'][2] * annotation['bbox'][3] + annotation['category_id'] = label_to_num[label] + annotation['id'] = object_num + 1 + return annotation + + +def annotations_polygon(height, width, points, label, image_num, object_num, label_to_num): + annotation = {} + annotation['segmentation'] = [list(np.asarray(points).flatten())] + annotation['iscrowd'] = 0 + annotation['image_id'] = image_num + 1 + annotation['bbox'] = list(map(float, get_bbox(height, width, points))) + annotation['area'] = annotation['bbox'][2] * annotation['bbox'][3] + annotation['category_id'] = label_to_num[label] + annotation['id'] = object_num + 1 + return annotation + + +def get_bbox(height, width, points): + polygons = points + mask = np.zeros([height, width], dtype=np.uint8) + mask = PIL.Image.fromarray(mask) + xy = list(map(tuple, polygons)) + PIL.ImageDraw.Draw(mask).polygon(xy=xy, outline=1, fill=1) + mask = np.array(mask, dtype=bool) + index = np.argwhere(mask == 1) + rows = index[:, 0] + clos = index[:, 1] + left_top_r = np.min(rows) + left_top_c = np.min(clos) + right_bottom_r = np.max(rows) + right_bottom_c = np.max(clos) + return [ + left_top_c, left_top_r, right_bottom_c - left_top_c, + right_bottom_r - left_top_r + ] + + +def deal_json(ds_type, img_path, json_path): + data_coco = {} + label_to_num = {} + images_list = [] + categories_list = [] + annotations_list = [] + labels_list = [] + image_num = -1 + object_num = -1 + for img_file in os.listdir(img_path): + img_label = img_file.split('.')[0] + if img_file.split('.')[-1] not in ['bmp', 'jpg', 'jpeg', 'png', 'JPEG', 'JPG', 'PNG']: + continue + label_file = osp.join(json_path, img_label + '.json') + print('Generating dataset from:', label_file) + image_num = image_num + 1 + with open(label_file) as f: + data = json.load(f) + if ds_type == 'labelme': + images_list.append(images_labelme(data, image_num)) + elif ds_type == 'cityscape': + images_list.append(images_cityscape(data, image_num, img_file)) + if ds_type == 'labelme': + for shapes in data['shapes']: + object_num = object_num + 1 + label = shapes['label'] + if label not in labels_list: + categories_list.append(categories(label, labels_list)) + labels_list.append(label) + label_to_num[label] = len(labels_list) + points = shapes['points'] + p_type = shapes['shape_type'] + if p_type == 'polygon': + annotations_list.append( + annotations_polygon(data['imageHeight'], data[ + 'imageWidth'], points, label, image_num, object_num, label_to_num)) + + if p_type == 'rectangle': + points.append([points[0][0], points[1][1]]) + points.append([points[1][0], points[0][1]]) + annotations_list.append( + annotations_rectangle(points, label, image_num, object_num, label_to_num)) + elif ds_type == 'cityscape': + for shapes in data['objects']: + object_num = object_num + 1 + label = shapes['label'] + if label not in labels_list: + categories_list.append(categories(label, labels_list)) + labels_list.append(label) + label_to_num[label] = len(labels_list) + points = shapes['polygon'] + annotations_list.append( + annotations_polygon(data['imgHeight'], data[ + 'imgWidth'], points, label, image_num, object_num, label_to_num)) + data_coco['images'] = images_list + data_coco['categories'] = categories_list + data_coco['annotations'] = annotations_list + return data_coco + + +def main(): + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('--dataset_type', help='the type of dataset') + parser.add_argument('--json_input_dir', help='input annotated directory') + parser.add_argument('--image_input_dir', help='image directory') + parser.add_argument( + '--output_dir', help='output dataset directory', default='../../../') + parser.add_argument( + '--train_proportion', + help='the proportion of train dataset', + type=float, + default=1.0) + parser.add_argument( + '--val_proportion', + help='the proportion of validation dataset', + type=float, + default=0.0) + parser.add_argument( + '--test_proportion', + help='the proportion of test dataset', + type=float, + default=0.0) + args = parser.parse_args() + try: + assert args.dataset_type in ['labelme', 'cityscape'] + except AssertionError as e: + print('Now only support the cityscape dataset and labelme dataset!!') + os._exit(0) + try: + assert os.path.exists(args.json_input_dir) + except AssertionError as e: + print('The json folder does not exist!') + os._exit(0) + try: + assert os.path.exists(args.image_input_dir) + except AssertionError as e: + print('The image folder does not exist!') + os._exit(0) + try: + assert args.train_proportion + args.val_proportion + args.test_proportion == 1.0 + except AssertionError as e: + print( + 'The sum of pqoportion of training, validation and test datase must be 1!' + ) + os._exit(0) + + # Allocate the dataset. + total_num = len(glob.glob(osp.join(args.json_input_dir, '*.json'))) + if args.train_proportion != 0: + train_num = int(total_num * args.train_proportion) + os.makedirs(args.output_dir + '/train') + else: + train_num = 0 + if args.val_proportion == 0.0: + val_num = 0 + test_num = total_num - train_num + if args.test_proportion != 0.0: + os.makedirs(args.output_dir + '/test') + else: + val_num = int(total_num * args.val_proportion) + test_num = total_num - train_num - val_num + os.makedirs(args.output_dir + '/val') + if args.test_proportion != 0.0: + os.makedirs(args.output_dir + '/test') + count = 1 + for img_name in os.listdir(args.image_input_dir): + if count <= train_num: + shutil.copyfile( + osp.join(args.image_input_dir, img_name), + osp.join(args.output_dir + '/train/', img_name)) + else: + if count <= train_num + val_num: + shutil.copyfile( + osp.join(args.image_input_dir, img_name), + osp.join(args.output_dir + '/val/', img_name)) + else: + shutil.copyfile( + osp.join(args.image_input_dir, img_name), + osp.join(args.output_dir + '/test/', img_name)) + count = count + 1 + + # Deal with the json files. + if not os.path.exists(args.output_dir + '/annotations'): + os.makedirs(args.output_dir + '/annotations') + if args.train_proportion != 0: + train_data_coco = deal_json(args.dataset_type, + args.output_dir + '/train', + args.json_input_dir) + train_json_path = osp.join(args.output_dir + '/annotations', + 'instance_train.json') + json.dump( + train_data_coco, + open(train_json_path, 'w'), + indent=4, + cls=MyEncoder) + if args.val_proportion != 0: + val_data_coco = deal_json(args.output_dir + '/val', args.json_input_dir) + val_json_path = osp.join(args.output_dir + '/annotations', + 'instance_val.json') + json.dump( + val_data_coco, open(val_json_path, 'w'), indent=4, cls=MyEncoder) + if args.test_proportion != 0: + test_data_coco = deal_json(args.output_dir + '/test', + args.json_input_dir) + test_json_path = osp.join(args.output_dir + '/annotations', + 'instance_test.json') + json.dump( + test_data_coco, open(test_json_path, 'w'), indent=4, cls=MyEncoder) + +if __name__ == '__main__': + main() diff --git a/ppdet/data/transform/__init__.py b/ppdet/data/transform/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f4d15e9c3354c6c44912c456eb766eae4d7d32c5 --- /dev/null +++ b/ppdet/data/transform/__init__.py @@ -0,0 +1,143 @@ +# 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 + +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 + +__all__ += registered_ops diff --git a/ppdet/data/transform/arrange_sample.py b/ppdet/data/transform/arrange_sample.py new file mode 100644 index 0000000000000000000000000000000000000000..bebce691d36ddb12141dd7bfdf81030ff8ed2d1f --- /dev/null +++ b/ppdet/data/transform/arrange_sample.py @@ -0,0 +1,384 @@ +# 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/op_helper.py b/ppdet/data/transform/op_helper.py new file mode 100644 index 0000000000000000000000000000000000000000..838714f4dda2b664ae4d2b1f3ee343e5b6e50360 --- /dev/null +++ b/ppdet/data/transform/op_helper.py @@ -0,0 +1,389 @@ +# 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. +# this file contains helper methods for BBOX processing + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import numpy as np +import random +import math +import cv2 + + +def meet_emit_constraint(src_bbox, sample_bbox): + center_x = (src_bbox[2] + src_bbox[0]) / 2 + center_y = (src_bbox[3] + src_bbox[1]) / 2 + if center_x >= sample_bbox[0] and \ + center_x <= sample_bbox[2] and \ + center_y >= sample_bbox[1] and \ + center_y <= sample_bbox[3]: + return True + return False + + +def clip_bbox(src_bbox): + src_bbox[0] = max(min(src_bbox[0], 1.0), 0.0) + src_bbox[1] = max(min(src_bbox[1], 1.0), 0.0) + src_bbox[2] = max(min(src_bbox[2], 1.0), 0.0) + src_bbox[3] = max(min(src_bbox[3], 1.0), 0.0) + return src_bbox + + +def bbox_area(src_bbox): + if src_bbox[2] < src_bbox[0] or src_bbox[3] < src_bbox[1]: + return 0. + else: + width = src_bbox[2] - src_bbox[0] + height = src_bbox[3] - src_bbox[1] + return width * height + + +def is_overlap(object_bbox, sample_bbox): + if object_bbox[0] >= sample_bbox[2] or \ + object_bbox[2] <= sample_bbox[0] or \ + object_bbox[1] >= sample_bbox[3] or \ + object_bbox[3] <= sample_bbox[1]: + return False + else: + return True + + +def filter_and_process(sample_bbox, bboxes, labels, scores=None): + new_bboxes = [] + new_labels = [] + new_scores = [] + for i in range(len(bboxes)): + new_bbox = [0, 0, 0, 0] + obj_bbox = [bboxes[i][0], bboxes[i][1], bboxes[i][2], bboxes[i][3]] + if not meet_emit_constraint(obj_bbox, sample_bbox): + continue + if not is_overlap(obj_bbox, sample_bbox): + continue + sample_width = sample_bbox[2] - sample_bbox[0] + sample_height = sample_bbox[3] - sample_bbox[1] + new_bbox[0] = (obj_bbox[0] - sample_bbox[0]) / sample_width + new_bbox[1] = (obj_bbox[1] - sample_bbox[1]) / sample_height + new_bbox[2] = (obj_bbox[2] - sample_bbox[0]) / sample_width + new_bbox[3] = (obj_bbox[3] - sample_bbox[1]) / sample_height + new_bbox = clip_bbox(new_bbox) + if bbox_area(new_bbox) > 0: + new_bboxes.append(new_bbox) + new_labels.append([labels[i][0]]) + if scores is not None: + new_scores.append([scores[i][0]]) + bboxes = np.array(new_bboxes) + labels = np.array(new_labels) + scores = np.array(new_scores) + return bboxes, labels, scores + + +def bbox_area_sampling(bboxes, labels, scores, target_size, min_size): + new_bboxes = [] + new_labels = [] + new_scores = [] + for i, bbox in enumerate(bboxes): + w = float((bbox[2] - bbox[0]) * target_size) + h = float((bbox[3] - bbox[1]) * target_size) + if w * h < float(min_size * min_size): + continue + else: + new_bboxes.append(bbox) + new_labels.append(labels[i]) + if scores is not None and scores.size != 0: + new_scores.append(scores[i]) + bboxes = np.array(new_bboxes) + labels = np.array(new_labels) + scores = np.array(new_scores) + return bboxes, labels, scores + + +def generate_sample_bbox(sampler): + scale = np.random.uniform(sampler[2], sampler[3]) + aspect_ratio = np.random.uniform(sampler[4], sampler[5]) + aspect_ratio = max(aspect_ratio, (scale**2.0)) + aspect_ratio = min(aspect_ratio, 1 / (scale**2.0)) + bbox_width = scale * (aspect_ratio**0.5) + bbox_height = scale / (aspect_ratio**0.5) + xmin_bound = 1 - bbox_width + ymin_bound = 1 - bbox_height + xmin = np.random.uniform(0, xmin_bound) + ymin = np.random.uniform(0, ymin_bound) + xmax = xmin + bbox_width + ymax = ymin + bbox_height + sampled_bbox = [xmin, ymin, xmax, ymax] + return sampled_bbox + + +def generate_sample_bbox_square(sampler, image_width, image_height): + scale = np.random.uniform(sampler[2], sampler[3]) + aspect_ratio = np.random.uniform(sampler[4], sampler[5]) + aspect_ratio = max(aspect_ratio, (scale**2.0)) + aspect_ratio = min(aspect_ratio, 1 / (scale**2.0)) + bbox_width = scale * (aspect_ratio**0.5) + bbox_height = scale / (aspect_ratio**0.5) + if image_height < image_width: + bbox_width = bbox_height * image_height / image_width + else: + bbox_height = bbox_width * image_width / image_height + xmin_bound = 1 - bbox_width + ymin_bound = 1 - bbox_height + xmin = np.random.uniform(0, xmin_bound) + ymin = np.random.uniform(0, ymin_bound) + xmax = xmin + bbox_width + ymax = ymin + bbox_height + sampled_bbox = [xmin, ymin, xmax, ymax] + return sampled_bbox + + +def data_anchor_sampling(bbox_labels, image_width, image_height, scale_array, + resize_width): + num_gt = len(bbox_labels) + # np.random.randint range: [low, high) + rand_idx = np.random.randint(0, num_gt) if num_gt != 0 else 0 + + if num_gt != 0: + norm_xmin = bbox_labels[rand_idx][0] + norm_ymin = bbox_labels[rand_idx][1] + norm_xmax = bbox_labels[rand_idx][2] + norm_ymax = bbox_labels[rand_idx][3] + + xmin = norm_xmin * image_width + ymin = norm_ymin * image_height + wid = image_width * (norm_xmax - norm_xmin) + hei = image_height * (norm_ymax - norm_ymin) + range_size = 0 + + area = wid * hei + for scale_ind in range(0, len(scale_array) - 1): + if area > scale_array[scale_ind] ** 2 and area < \ + scale_array[scale_ind + 1] ** 2: + range_size = scale_ind + 1 + break + + if area > scale_array[len(scale_array) - 2]**2: + range_size = len(scale_array) - 2 + + scale_choose = 0.0 + if range_size == 0: + rand_idx_size = 0 + else: + # np.random.randint range: [low, high) + rng_rand_size = np.random.randint(0, range_size + 1) + rand_idx_size = rng_rand_size % (range_size + 1) + + if rand_idx_size == range_size: + min_resize_val = scale_array[rand_idx_size] / 2.0 + max_resize_val = min(2.0 * scale_array[rand_idx_size], + 2 * math.sqrt(wid * hei)) + scale_choose = random.uniform(min_resize_val, max_resize_val) + else: + min_resize_val = scale_array[rand_idx_size] / 2.0 + max_resize_val = 2.0 * scale_array[rand_idx_size] + scale_choose = random.uniform(min_resize_val, max_resize_val) + + sample_bbox_size = wid * resize_width / scale_choose + + w_off_orig = 0.0 + h_off_orig = 0.0 + if sample_bbox_size < max(image_height, image_width): + if wid <= sample_bbox_size: + w_off_orig = np.random.uniform(xmin + wid - sample_bbox_size, + xmin) + else: + w_off_orig = np.random.uniform(xmin, + xmin + wid - sample_bbox_size) + + if hei <= sample_bbox_size: + h_off_orig = np.random.uniform(ymin + hei - sample_bbox_size, + ymin) + else: + h_off_orig = np.random.uniform(ymin, + ymin + hei - sample_bbox_size) + + else: + w_off_orig = np.random.uniform(image_width - sample_bbox_size, 0.0) + h_off_orig = np.random.uniform(image_height - sample_bbox_size, 0.0) + + w_off_orig = math.floor(w_off_orig) + h_off_orig = math.floor(h_off_orig) + + # Figure out top left coordinates. + w_off = float(w_off_orig / image_width) + h_off = float(h_off_orig / image_height) + + sampled_bbox = [ + w_off, h_off, w_off + float(sample_bbox_size / image_width), + h_off + float(sample_bbox_size / image_height) + ] + return sampled_bbox + else: + return 0 + + +def jaccard_overlap(sample_bbox, object_bbox): + if sample_bbox[0] >= object_bbox[2] or \ + sample_bbox[2] <= object_bbox[0] or \ + sample_bbox[1] >= object_bbox[3] or \ + sample_bbox[3] <= object_bbox[1]: + return 0 + intersect_xmin = max(sample_bbox[0], object_bbox[0]) + intersect_ymin = max(sample_bbox[1], object_bbox[1]) + intersect_xmax = min(sample_bbox[2], object_bbox[2]) + intersect_ymax = min(sample_bbox[3], object_bbox[3]) + intersect_size = (intersect_xmax - intersect_xmin) * ( + intersect_ymax - intersect_ymin) + sample_bbox_size = bbox_area(sample_bbox) + object_bbox_size = bbox_area(object_bbox) + overlap = intersect_size / ( + sample_bbox_size + object_bbox_size - intersect_size) + return overlap + + +def intersect_bbox(bbox1, bbox2): + if bbox2[0] > bbox1[2] or bbox2[2] < bbox1[0] or \ + bbox2[1] > bbox1[3] or bbox2[3] < bbox1[1]: + intersection_box = [0.0, 0.0, 0.0, 0.0] + else: + intersection_box = [ + max(bbox1[0], bbox2[0]), max(bbox1[1], bbox2[1]), + min(bbox1[2], bbox2[2]), min(bbox1[3], bbox2[3]) + ] + return intersection_box + + +def bbox_coverage(bbox1, bbox2): + inter_box = intersect_bbox(bbox1, bbox2) + intersect_size = bbox_area(inter_box) + + if intersect_size > 0: + bbox1_size = bbox_area(bbox1) + return intersect_size / bbox1_size + else: + return 0. + + +def satisfy_sample_constraint(sampler, + sample_bbox, + gt_bboxes, + satisfy_all=False): + if sampler[6] == 0 and sampler[7] == 0: + return True + satisfied = [] + for i in range(len(gt_bboxes)): + object_bbox = [ + gt_bboxes[i][0], gt_bboxes[i][1], gt_bboxes[i][2], gt_bboxes[i][3] + ] + overlap = jaccard_overlap(sample_bbox, object_bbox) + if sampler[6] != 0 and \ + overlap < sampler[6]: + satisfied.append(False) + continue + if sampler[7] != 0 and \ + overlap > sampler[7]: + satisfied.append(False) + continue + satisfied.append(True) + if not satisfy_all: + return True + + if satisfy_all: + return np.all(satisfied) + else: + return False + + +def satisfy_sample_constraint_coverage(sampler, sample_bbox, gt_bboxes): + if sampler[6] == 0 and sampler[7] == 0: + has_jaccard_overlap = False + else: + has_jaccard_overlap = True + if sampler[8] == 0 and sampler[9] == 0: + has_object_coverage = False + else: + has_object_coverage = True + + if not has_jaccard_overlap and not has_object_coverage: + return True + found = False + for i in range(len(gt_bboxes)): + object_bbox = [ + gt_bboxes[i][0], gt_bboxes[i][1], gt_bboxes[i][2], gt_bboxes[i][3] + ] + if has_jaccard_overlap: + overlap = jaccard_overlap(sample_bbox, object_bbox) + if sampler[6] != 0 and \ + overlap < sampler[6]: + continue + if sampler[7] != 0 and \ + overlap > sampler[7]: + continue + found = True + if has_object_coverage: + object_coverage = bbox_coverage(object_bbox, sample_bbox) + if sampler[8] != 0 and \ + object_coverage < sampler[8]: + continue + if sampler[9] != 0 and \ + object_coverage > sampler[9]: + continue + found = True + if found: + return True + return found + + +def crop_image_sampling(img, sample_bbox, image_width, image_height, + target_size): + # no clipping here + xmin = int(sample_bbox[0] * image_width) + xmax = int(sample_bbox[2] * image_width) + ymin = int(sample_bbox[1] * image_height) + ymax = int(sample_bbox[3] * image_height) + + w_off = xmin + h_off = ymin + width = xmax - xmin + height = ymax - ymin + cross_xmin = max(0.0, float(w_off)) + cross_ymin = max(0.0, float(h_off)) + cross_xmax = min(float(w_off + width - 1.0), float(image_width)) + cross_ymax = min(float(h_off + height - 1.0), float(image_height)) + cross_width = cross_xmax - cross_xmin + cross_height = cross_ymax - cross_ymin + + roi_xmin = 0 if w_off >= 0 else abs(w_off) + roi_ymin = 0 if h_off >= 0 else abs(h_off) + roi_width = cross_width + roi_height = cross_height + + roi_y1 = int(roi_ymin) + roi_y2 = int(roi_ymin + roi_height) + roi_x1 = int(roi_xmin) + roi_x2 = int(roi_xmin + roi_width) + + cross_y1 = int(cross_ymin) + cross_y2 = int(cross_ymin + cross_height) + cross_x1 = int(cross_xmin) + cross_x2 = int(cross_xmin + cross_width) + + sample_img = np.zeros((height, width, 3)) + sample_img[roi_y1: roi_y2, roi_x1: roi_x2] = \ + img[cross_y1: cross_y2, cross_x1: cross_x2] + + sample_img = cv2.resize( + sample_img, (target_size, target_size), interpolation=cv2.INTER_AREA) + + return sample_img diff --git a/ppdet/data/transform/operators.py b/ppdet/data/transform/operators.py new file mode 100644 index 0000000000000000000000000000000000000000..b09998120ffc1e76e42299489f16bacc6424454c --- /dev/null +++ b/ppdet/data/transform/operators.py @@ -0,0 +1,991 @@ +# 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 print_function +from __future__ import division + +import uuid +import logging +import random +import math +import numpy as np +import cv2 +from PIL import Image, ImageEnhance + +from ppdet.core.workspace import serializable + +from .op_helper import (satisfy_sample_constraint, filter_and_process, + generate_sample_bbox, clip_bbox, data_anchor_sampling, + satisfy_sample_constraint_coverage, crop_image_sampling, + generate_sample_bbox_square, bbox_area_sampling) + +logger = logging.getLogger(__name__) + +registered_ops = [] + + +def register_op(cls): + registered_ops.append(cls.__name__) + if not hasattr(BaseOperator, cls.__name__): + setattr(BaseOperator, cls.__name__, cls) + else: + raise KeyError("The {} class has been registered.".format(cls.__name__)) + return serializable(cls) + + +class BboxError(ValueError): + pass + + +class ImageError(ValueError): + pass + + +class BaseOperator(object): + def __init__(self, name=None): + if name is None: + name = self.__class__.__name__ + self._id = name + '_' + str(uuid.uuid4())[-6:] + + def __call__(self, sample, context=None): + """ Process a sample. + Args: + sample (dict): a dict of sample, eg: {'image':xx, 'label': xxx} + context (dict): info about this sample processing + Returns: + result (dict): a processed sample + """ + return sample + + def __str__(self): + return str(self._id) + + +@register_op +class DecodeImage(BaseOperator): + def __init__(self, to_rgb=True, with_mixup=False): + """ Transform the image data to numpy format. + + Args: + to_rgb (bool): whether to convert BGR to RGB + with_mixup (bool): whether or not to mixup image and gt_bbbox/gt_score + """ + + super(DecodeImage, self).__init__() + self.to_rgb = to_rgb + self.with_mixup = with_mixup + if not isinstance(self.to_rgb, bool): + raise TypeError("{}: input type is invalid.".format(self)) + if not isinstance(self.with_mixup, bool): + raise TypeError("{}: input type is invalid.".format(self)) + + def __call__(self, sample, context=None): + """ load image if 'im_file' field is not empty but 'image' is""" + if 'image' not in sample: + with open(sample['im_file'], 'rb') as f: + sample['image'] = f.read() + + im = sample['image'] + data = np.frombuffer(im, dtype='uint8') + im = cv2.imdecode(data, 1) # BGR mode, but need RGB mode + if self.to_rgb: + im = cv2.cvtColor(im, cv2.COLOR_BGR2RGB) + sample['image'] = im + + if 'h' not in sample: + sample['h'] = im.shape[0] + if 'w' not in sample: + sample['w'] = im.shape[1] + # make default im_info with [h, w, 1] + sample['im_info'] = np.array( + [im.shape[0], im.shape[1], 1.], dtype=np.float32) + # decode mixup image + if self.with_mixup and 'mixup' in sample: + self.__call__(sample['mixup'], context) + return sample + + +@register_op +class MultiscaleTestResize(BaseOperator): + def __init__(self, + origin_target_size=800, + origin_max_size=1333, + target_size=[], + max_size=2000, + interp=cv2.INTER_LINEAR, + use_flip=True): + """ + Rescale image to the each size in target size, and capped at max_size. + + Args: + origin_target_size(int): original target size of image's short side. + origin_max_size(int): original max size of image. + target_size (list): A list of target sizes of image's short side. + max_size (int): the max size of image. + interp (int): the interpolation method. + use_flip (bool): whether use flip augmentation. + """ + super(MultiscaleTestResize, self).__init__() + self.origin_target_size = int(origin_target_size) + self.origin_max_size = int(origin_max_size) + self.max_size = int(max_size) + self.interp = int(interp) + self.use_flip = use_flip + + if not isinstance(target_size, list): + raise TypeError( + "Type of target_size is invalid. Must be List, now is {}". + format(type(target_size))) + self.target_size = target_size + if not (isinstance(self.origin_target_size, int) and isinstance( + self.origin_max_size, int) and isinstance(self.max_size, int) + and isinstance(self.interp, int)): + raise TypeError("{}: input type is invalid.".format(self)) + + def __call__(self, sample, context=None): + """ Resize the image numpy for multi-scale test. + """ + origin_ims = {} + im = sample['image'] + if not isinstance(im, np.ndarray): + raise TypeError("{}: image type is not numpy.".format(self)) + if len(im.shape) != 3: + raise ImageError('{}: image is not 3-dimensional.'.format(self)) + im_shape = im.shape + im_size_min = np.min(im_shape[0:2]) + im_size_max = np.max(im_shape[0:2]) + if float(im_size_min) == 0: + raise ZeroDivisionError('{}: min size of image is 0'.format(self)) + 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 = [] + 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 + if np.round(im_scale * im_size_max) > self.origin_max_size: + im_scale = float(self.origin_max_size) / float(im_size_max) + im_scale_x = im_scale + im_scale_y = im_scale + + resize_w = np.round(im_scale_x * float(im_shape[1])) + resize_h = np.round(im_scale_y * float(im_shape[0])) + im_resize = cv2.resize( + origin_ims[base_name], + None, + None, + 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 + 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: + im_scale = float(self.max_size) / float(im_size_max) + im_scale_x = im_scale + im_scale_y = im_scale + resize_w = np.round(im_scale_x * float(im_shape[1])) + resize_h = np.round(im_scale_y * float(im_shape[0])) + im_resize = cv2.resize( + origin_ims[base_name], + None, + None, + fx=im_scale_x, + fy=im_scale_y, + interpolation=self.interp) + im_info.extend([resize_h, resize_w, im_scale]) + name = base_name + '_scale_' + str(i) + sample[name] = im_resize + sample['im_info'] = np.array(im_info, dtype=np.float32) + return sample + + +@register_op +class ResizeImage(BaseOperator): + def __init__(self, + target_size=0, + max_size=0, + interp=cv2.INTER_LINEAR, + use_cv2=True): + """ + Rescale image to the specified target size, and capped at max_size + if max_size != 0. + If target_size is list, selected a scale randomly as the specified + target size. + + Args: + target_size (int|list): the target size of image's short side, + multi-scale training is adopted when type is list. + max_size (int): the max size of image + interp (int): the interpolation method + use_cv2 (bool): use the cv2 interpolation method or use PIL + interpolation method + """ + super(ResizeImage, self).__init__() + self.max_size = int(max_size) + self.interp = int(interp) + self.use_cv2 = use_cv2 + if not (isinstance(target_size, int) or isinstance(target_size, list)): + raise TypeError( + "Type of target_size is invalid. Must be Integer or List, now is {}". + format(type(target_size))) + self.target_size = target_size + if not (isinstance(self.max_size, int) and isinstance(self.interp, + int)): + raise TypeError("{}: input type is invalid.".format(self)) + + def __call__(self, sample, context=None): + """ Resize the image numpy. + """ + im = sample['image'] + if not isinstance(im, np.ndarray): + raise TypeError("{}: image type is not numpy.".format(self)) + if len(im.shape) != 3: + raise ImageError('{}: image is not 3-dimensional.'.format(self)) + im_shape = im.shape + im_size_min = np.min(im_shape[0:2]) + im_size_max = np.max(im_shape[0:2]) + if isinstance(self.target_size, list): + # Case for multi-scale training + selected_size = random.choice(self.target_size) + else: + selected_size = self.target_size + if float(im_size_min) == 0: + raise ZeroDivisionError('{}: min size of image is 0'.format(self)) + if self.max_size != 0: + im_scale = float(selected_size) / float(im_size_min) + # Prevent the biggest axis from being more than max_size + if np.round(im_scale * im_size_max) > self.max_size: + im_scale = float(self.max_size) / float(im_size_max) + im_scale_x = im_scale + im_scale_y = im_scale + + resize_w = np.round(im_scale_x * float(im_shape[1])) + resize_h = np.round(im_scale_y * float(im_shape[0])) + im_info = [resize_h, resize_w, im_scale] + if 'im_info' in sample and sample['im_info'][2] != 1.: + sample['im_info'] = np.append( + list(sample['im_info']), im_info).astype(np.float32) + else: + sample['im_info'] = np.array(im_info).astype(np.float32) + else: + im_scale_x = float(selected_size) / float(im_shape[1]) + im_scale_y = float(selected_size) / float(im_shape[0]) + + resize_w = selected_size + resize_h = selected_size + + if self.use_cv2: + im = cv2.resize( + im, + None, + None, + fx=im_scale_x, + fy=im_scale_y, + interpolation=self.interp) + else: + im = Image.fromarray(im) + im = im.resize((resize_w, resize_h), self.interp) + im = np.array(im) + + sample['image'] = im + return sample + + +@register_op +class RandomFlipImage(BaseOperator): + def __init__(self, prob=0.5, is_normalized=False, is_mask_flip=False): + """ + Args: + prob (float): the probability of flipping image + is_normalized (bool): whether the bbox scale to [0,1] + is_mask_flip (bool): whether flip the segmentation + """ + super(RandomFlipImage, self).__init__() + self.prob = prob + self.is_normalized = is_normalized + self.is_mask_flip = is_mask_flip + if not (isinstance(self.prob, float) and + isinstance(self.is_normalized, bool) and + isinstance(self.is_mask_flip, bool)): + raise TypeError("{}: input type is invalid.".format(self)) + + def flip_segms(self, segms, height, width): + def _flip_poly(poly, width): + flipped_poly = np.array(poly) + flipped_poly[0::2] = width - np.array(poly[0::2]) - 1 + return flipped_poly.tolist() + + def _flip_rle(rle, height, width): + if 'counts' in rle and type(rle['counts']) == list: + rle = mask_util.frPyObjects([rle], height, width) + mask = mask_util.decode(rle) + mask = mask[:, ::-1, :] + rle = mask_util.encode(np.array(mask, order='F', dtype=np.uint8)) + return rle + + def is_poly(segm): + assert isinstance(segm, (list, dict)), \ + "Invalid segm type: {}".format(type(segm)) + return isinstance(segm, list) + + flipped_segms = [] + for segm in segms: + if is_poly(segm): + # Polygon format + flipped_segms.append([_flip_poly(poly, width) for poly in segm]) + else: + # RLE format + import pycocotools.mask as mask_util + flipped_segms.append(_flip_rle(segm, height, width)) + return flipped_segms + + def __call__(self, sample, context=None): + """Filp the image and bounding box. + Operators: + 1. Flip the image numpy. + 2. Transform the bboxes' x coordinates. + (Must judge whether the coordinates are normalized!) + 3. Transform the segmentations' x coordinates. + (Must judge whether the coordinates are normalized!) + Output: + 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 + return sample + + +@register_op +class NormalizeImage(BaseOperator): + def __init__(self, + mean=[0.485, 0.456, 0.406], + std=[1, 1, 1], + is_scale=True, + is_channel_first=True): + """ + Args: + mean (list): the pixel mean + std (list): the pixel variance + """ + super(NormalizeImage, self).__init__() + self.mean = mean + self.std = std + self.is_scale = is_scale + self.is_channel_first = is_channel_first + if not (isinstance(self.mean, list) and isinstance(self.std, list) and + isinstance(self.is_scale, bool)): + raise TypeError("{}: input type is invalid.".format(self)) + from functools import reduce + if reduce(lambda x, y: x * y, self.std) == 0: + raise ValueError('{}: std is invalid!'.format(self)) + + def __call__(self, sample, context=None): + """Normalize the image. + Operators: + 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 + + +@register_op +class RandomDistort(BaseOperator): + def __init__(self, + brightness_lower=0.5, + brightness_upper=1.5, + contrast_lower=0.5, + contrast_upper=1.5, + saturation_lower=0.5, + saturation_upper=1.5, + hue_lower=-18, + hue_upper=18, + brightness_prob=0.5, + contrast_prob=0.5, + saturation_prob=0.5, + hue_prob=0.5, + count=4, + is_order=False): + """ + Args: + brightness_lower/ brightness_upper (float): the brightness + between brightness_lower and brightness_upper + contrast_lower/ contrast_upper (float): the contrast between + contrast_lower and contrast_lower + saturation_lower/ saturation_upper (float): the saturation + between saturation_lower and saturation_upper + hue_lower/ hue_upper (float): the hue between + hue_lower and hue_upper + brightness_prob (float): the probability of changing brightness + contrast_prob (float): the probability of changing contrast + saturation_prob (float): the probability of changing saturation + hue_prob (float): the probability of changing hue + count (int): the kinds of doing distrot + is_order (bool): whether determine the order of distortion + """ + super(RandomDistort, self).__init__() + self.brightness_lower = brightness_lower + self.brightness_upper = brightness_upper + self.contrast_lower = contrast_lower + self.contrast_upper = contrast_upper + self.saturation_lower = saturation_lower + self.saturation_upper = saturation_upper + self.hue_lower = hue_lower + self.hue_upper = hue_upper + self.brightness_prob = brightness_prob + self.contrast_prob = contrast_prob + self.saturation_prob = saturation_prob + self.hue_prob = hue_prob + self.count = count + self.is_order = is_order + + def random_brightness(self, img): + brightness_delta = np.random.uniform(self.brightness_lower, + self.brightness_upper) + prob = np.random.uniform(0, 1) + if prob < self.brightness_prob: + img = ImageEnhance.Brightness(img).enhance(brightness_delta) + return img + + def random_contrast(self, img): + contrast_delta = np.random.uniform(self.contrast_lower, + self.contrast_upper) + prob = np.random.uniform(0, 1) + if prob < self.contrast_prob: + img = ImageEnhance.Contrast(img).enhance(contrast_delta) + return img + + def random_saturation(self, img): + saturation_delta = np.random.uniform(self.saturation_lower, + self.saturation_upper) + prob = np.random.uniform(0, 1) + if prob < self.saturation_prob: + img = ImageEnhance.Color(img).enhance(saturation_delta) + return img + + def random_hue(self, img): + hue_delta = np.random.uniform(self.hue_lower, self.hue_upper) + prob = np.random.uniform(0, 1) + if prob < self.hue_prob: + img = np.array(img.convert('HSV')) + img[:, :, 0] = img[:, :, 0] + hue_delta + img = Image.fromarray(img, mode='HSV').convert('RGB') + return img + + def __call__(self, sample, context): + """random distort the image""" + ops = [ + self.random_brightness, self.random_contrast, + self.random_saturation, self.random_hue + ] + if self.is_order: + prob = np.random.uniform(0, 1) + if prob < 0.5: + ops = [ + self.random_brightness, + self.random_saturation, + self.random_hue, + self.random_contrast, + ] + else: + ops = random.sample(ops, self.count) + assert 'image' in sample, "image data not found" + im = sample['image'] + im = Image.fromarray(im) + for id in range(self.count): + im = ops[id](im) + im = np.asarray(im) + sample['image'] = im + return sample + + +@register_op +class ExpandImage(BaseOperator): + def __init__(self, max_ratio, prob, mean=[127.5, 127.5, 127.5]): + """ + Args: + max_ratio (float): the ratio of expanding + prob (float): the probability of expanding image + mean (list): the pixel mean + """ + super(ExpandImage, self).__init__() + self.max_ratio = max_ratio + self.mean = mean + self.prob = prob + + def __call__(self, sample, context): + """ + Expand the image and modify bounding box. + Operators: + 1. Scale the image width and height. + 2. Construct new images with new height and width. + 3. Fill the new image with the mean. + 4. Put original imge into new image. + 5. Rescale the bounding box. + 6. Determine if the new bbox is satisfied in the new image. + Returns: + sample: the image, bounding box are replaced. + """ + + prob = np.random.uniform(0, 1) + assert 'image' in sample, 'not found image data' + im = sample['image'] + gt_bbox = sample['gt_bbox'] + gt_class = sample['gt_class'] + im_width = sample['w'] + im_height = sample['h'] + if prob < self.prob: + if self.max_ratio - 1 >= 0.01: + expand_ratio = np.random.uniform(1, self.max_ratio) + height = int(im_height * expand_ratio) + width = int(im_width * expand_ratio) + h_off = math.floor(np.random.uniform(0, height - im_height)) + w_off = math.floor(np.random.uniform(0, width - im_width)) + expand_bbox = [ + -w_off / im_width, -h_off / im_height, + (width - w_off) / im_width, (height - h_off) / im_height + ] + expand_im = np.ones((height, width, 3)) + expand_im = np.uint8(expand_im * np.squeeze(self.mean)) + expand_im = Image.fromarray(expand_im) + im = Image.fromarray(im) + expand_im.paste(im, (int(w_off), int(h_off))) + expand_im = np.asarray(expand_im) + gt_bbox, gt_class, _ = filter_and_process(expand_bbox, gt_bbox, + gt_class) + sample['image'] = expand_im + sample['gt_bbox'] = gt_bbox + sample['gt_class'] = gt_class + sample['w'] = width + sample['h'] = height + + return sample + + +@register_op +class CropImage(BaseOperator): + def __init__(self, batch_sampler, satisfy_all=False, avoid_no_bbox=True): + """ + Args: + batch_sampler (list): Multiple sets of different + parameters for cropping. + satisfy_all (bool): whether all boxes must satisfy. + e.g.[[1, 1, 1.0, 1.0, 1.0, 1.0, 0.0, 1.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]] + [max sample, max trial, min scale, max scale, + min aspect ratio, max aspect ratio, + min overlap, max overlap] + avoid_no_bbox (bool): whether to to avoid the + situation where the box does not appear. + """ + super(CropImage, self).__init__() + self.batch_sampler = batch_sampler + self.satisfy_all = satisfy_all + self.avoid_no_bbox = avoid_no_bbox + + def __call__(self, sample, context): + """ + Crop the image and modify bounding box. + Operators: + 1. Scale the image width and height. + 2. Crop the image according to a radom sample. + 3. Rescale the bounding box. + 4. Determine if the new bbox is satisfied in the new image. + Returns: + sample: the image, bounding box are replaced. + """ + assert 'image' in sample, "image data not found" + im = sample['image'] + gt_bbox = sample['gt_bbox'] + gt_class = sample['gt_class'] + im_width = sample['w'] + im_height = sample['h'] + gt_score = None + if 'gt_score' in sample: + gt_score = sample['gt_score'] + sampled_bbox = [] + gt_bbox = gt_bbox.tolist() + for sampler in self.batch_sampler: + found = 0 + for i in range(sampler[1]): + if found >= sampler[0]: + break + sample_bbox = generate_sample_bbox(sampler) + if satisfy_sample_constraint(sampler, sample_bbox, gt_bbox, + self.satisfy_all): + sampled_bbox.append(sample_bbox) + found = found + 1 + im = np.array(im) + while sampled_bbox: + idx = int(np.random.uniform(0, len(sampled_bbox))) + sample_bbox = sampled_bbox.pop(idx) + sample_bbox = clip_bbox(sample_bbox) + crop_bbox, crop_class, crop_score = \ + filter_and_process(sample_bbox, gt_bbox, gt_class, gt_score) + if self.avoid_no_bbox: + if len(crop_bbox) < 1: + continue + xmin = int(sample_bbox[0] * im_width) + xmax = int(sample_bbox[2] * im_width) + ymin = int(sample_bbox[1] * im_height) + ymax = int(sample_bbox[3] * im_height) + im = im[ymin:ymax, xmin:xmax] + sample['image'] = im + sample['gt_bbox'] = crop_bbox + sample['gt_class'] = crop_class + sample['gt_score'] = crop_score + return sample + return sample + + +@register_op +class CropImageWithDataAchorSampling(BaseOperator): + def __init__(self, + batch_sampler, + anchor_sampler=None, + target_size=None, + das_anchor_scales=[16, 32, 64, 128], + sampling_prob=0.5, + min_size=8., + avoid_no_bbox=True): + """ + Args: + anchor_sampler (list): anchor_sampling sets of different + parameters for cropping. + batch_sampler (list): Multiple sets of different + parameters for cropping. + e.g.[[1, 10, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.2, 0.0]] + [[1, 50, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0], + [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0], + [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0], + [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0], + [1, 50, 0.3, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0]] + [max sample, max trial, min scale, max scale, + min aspect ratio, max aspect ratio, + min overlap, max overlap, min coverage, max coverage] + target_size (bool): target image size. + das_anchor_scales (list[float]): a list of anchor scales in data + anchor smapling. + min_size (float): minimum size of sampled bbox. + avoid_no_bbox (bool): whether to to avoid the + situation where the box does not appear. + """ + super(CropImageWithDataAchorSampling, self).__init__() + self.anchor_sampler = anchor_sampler + self.batch_sampler = batch_sampler + self.target_size = target_size + self.sampling_prob = sampling_prob + self.min_size = min_size + self.avoid_no_bbox = avoid_no_bbox + self.das_anchor_scales = np.array(das_anchor_scales) + + def __call__(self, sample, context): + """ + Crop the image and modify bounding box. + Operators: + 1. Scale the image width and height. + 2. Crop the image according to a radom sample. + 3. Rescale the bounding box. + 4. Determine if the new bbox is satisfied in the new image. + Returns: + sample: the image, bounding box are replaced. + """ + assert 'image' in sample, "image data not found" + im = sample['image'] + gt_bbox = sample['gt_bbox'] + gt_class = sample['gt_class'] + image_width = sample['w'] + image_height = sample['h'] + gt_score = None + if 'gt_score' in sample: + gt_score = sample['gt_score'] + sampled_bbox = [] + gt_bbox = gt_bbox.tolist() + + prob = np.random.uniform(0., 1.) + if prob > self.sampling_prob: # anchor sampling + assert self.anchor_sampler + for sampler in self.anchor_sampler: + found = 0 + for i in range(sampler[1]): + if found >= sampler[0]: + break + sample_bbox = data_anchor_sampling( + gt_bbox, image_width, image_height, + self.das_anchor_scales, self.target_size) + if sample_bbox == 0: + break + if satisfy_sample_constraint_coverage(sampler, sample_bbox, + gt_bbox): + sampled_bbox.append(sample_bbox) + found = found + 1 + im = np.array(im) + while sampled_bbox: + idx = int(np.random.uniform(0, len(sampled_bbox))) + sample_bbox = sampled_bbox.pop(idx) + + crop_bbox, crop_class, crop_score = filter_and_process( + sample_bbox, gt_bbox, gt_class, gt_score) + crop_bbox, crop_class, crop_score = bbox_area_sampling( + crop_bbox, crop_class, crop_score, self.target_size, + self.min_size) + + if self.avoid_no_bbox: + if len(crop_bbox) < 1: + continue + im = crop_image_sampling(im, sample_bbox, image_width, + image_height, self.target_size) + sample['image'] = im + sample['gt_bbox'] = crop_bbox + sample['gt_class'] = crop_class + sample['gt_score'] = crop_score + return sample + return sample + + else: + for sampler in self.batch_sampler: + found = 0 + for i in range(sampler[1]): + if found >= sampler[0]: + break + sample_bbox = generate_sample_bbox_square( + sampler, image_width, image_height) + if satisfy_sample_constraint_coverage(sampler, sample_bbox, + gt_bbox): + sampled_bbox.append(sample_bbox) + found = found + 1 + im = np.array(im) + while sampled_bbox: + idx = int(np.random.uniform(0, len(sampled_bbox))) + sample_bbox = sampled_bbox.pop(idx) + sample_bbox = clip_bbox(sample_bbox) + + crop_bbox, crop_class, crop_score = filter_and_process( + sample_bbox, gt_bbox, gt_class, gt_score) + # sampling bbox according the bbox area + crop_bbox, crop_class, crop_score = bbox_area_sampling( + crop_bbox, crop_class, crop_score, self.target_size, + self.min_size) + + if self.avoid_no_bbox: + if len(crop_bbox) < 1: + continue + xmin = int(sample_bbox[0] * image_width) + xmax = int(sample_bbox[2] * image_width) + ymin = int(sample_bbox[1] * image_height) + ymax = int(sample_bbox[3] * image_height) + im = im[ymin:ymax, xmin:xmax] + sample['image'] = im + sample['gt_bbox'] = crop_bbox + sample['gt_class'] = crop_class + sample['gt_score'] = crop_score + return sample + return sample + + +@register_op +class NormalizeBox(BaseOperator): + """Transform the bounding box's coornidates to [0,1].""" + + def __init__(self): + super(NormalizeBox, self).__init__() + + def __call__(self, sample, context): + gt_bbox = sample['gt_bbox'] + width = sample['w'] + height = sample['h'] + for i in range(gt_bbox.shape[0]): + gt_bbox[i][0] = gt_bbox[i][0] / width + gt_bbox[i][1] = gt_bbox[i][1] / height + gt_bbox[i][2] = gt_bbox[i][2] / width + gt_bbox[i][3] = gt_bbox[i][3] / height + sample['gt_bbox'] = gt_bbox + return sample + + +@register_op +class Permute(BaseOperator): + def __init__(self, to_bgr=True, channel_first=True): + """ + Change the channel. + Args: + to_bgr (bool): confirm whether to convert RGB to BGR + channel_first (bool): confirm whether to change channel + + """ + super(Permute, self).__init__() + self.to_bgr = to_bgr + self.channel_first = channel_first + if not (isinstance(self.to_bgr, bool) and + isinstance(self.channel_first, bool)): + 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 + + +@register_op +class MixupImage(BaseOperator): + def __init__(self, alpha=1.5, beta=1.5): + """ Mixup image and gt_bbbox/gt_score + Args: + alpha (float): alpha parameter of beta distribute + beta (float): beta parameter of beta distribute + """ + super(MixupImage, self).__init__() + self.alpha = alpha + self.beta = beta + if self.alpha <= 0.0: + raise ValueError("alpha shold be positive in {}".format(self)) + if self.beta <= 0.0: + raise ValueError("beta shold be positive in {}".format(self)) + + def _mixup_img(self, img1, img2, factor): + h = max(img1.shape[0], img2.shape[0]) + w = max(img1.shape[1], img2.shape[1]) + img = np.zeros((h, w, img1.shape[2]), 'float32') + img[:img1.shape[0], :img1.shape[1], :] = \ + img1.astype('float32') * factor + img[:img2.shape[0], :img2.shape[1], :] += \ + img2.astype('float32') * (1.0 - factor) + return img.astype('uint8') + + def __call__(self, sample, context=None): + if 'mixup' not in sample: + return sample + factor = np.random.beta(self.alpha, self.beta) + factor = max(0.0, min(1.0, factor)) + if factor >= 1.0: + sample.pop('mixup') + return sample + if factor <= 0.0: + return sample['mixup'] + im = self._mixup_img(sample['image'], sample['mixup']['image'], factor) + gt_bbox1 = sample['gt_bbox'] + gt_bbox2 = sample['mixup']['gt_bbox'] + gt_bbox = np.concatenate((gt_bbox1, gt_bbox2), axis=0) + gt_class1 = sample['gt_class'] + gt_class2 = sample['mixup']['gt_class'] + gt_class = np.concatenate((gt_class1, gt_class2), axis=0) + + gt_score1 = sample['gt_score'] + gt_score2 = sample['mixup']['gt_score'] + gt_score = np.concatenate( + (gt_score1 * factor, gt_score2 * (1. - factor)), axis=0) + sample['image'] = im + sample['gt_bbox'] = gt_bbox + sample['gt_score'] = gt_score + sample['gt_class'] = gt_class + sample['h'] = im.shape[0] + sample['w'] = im.shape[1] + sample.pop('mixup') + return sample + + +@register_op +class RandomInterpImage(BaseOperator): + def __init__(self, target_size=0, max_size=0): + """ + Random reisze image by multiply interpolate method. + Args: + target_size (int): the taregt size of image's short side + max_size (int): the max size of image + """ + super(RandomInterpImage, self).__init__() + self.target_size = target_size + self.max_size = max_size + if not (isinstance(self.target_size, int) and + isinstance(self.max_size, int)): + raise TypeError('{}: input type is invalid.'.format(self)) + interps = [ + cv2.INTER_NEAREST, + cv2.INTER_LINEAR, + cv2.INTER_AREA, + cv2.INTER_CUBIC, + cv2.INTER_LANCZOS4, + ] + self.resizers = [] + for interp in interps: + self.resizers.append(ResizeImage(target_size, max_size, interp)) + + def __call__(self, sample, context=None): + """Resise the image numpy by random resizer.""" + resizer = random.choice(self.resizers) + return resizer(sample, context) diff --git a/ppdet/data/transform/parallel_map.py b/ppdet/data/transform/parallel_map.py new file mode 100644 index 0000000000000000000000000000000000000000..2ba55a8d203cfa7f467e1790b372cf6ce6e02fdb --- /dev/null +++ b/ppdet/data/transform/parallel_map.py @@ -0,0 +1,225 @@ +# 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: +# transform samples in 'source' using 'mapper' + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import sys +import six +import uuid +import logging +import signal +import threading +from .transformer import ProxiedDataset + +logger = logging.getLogger(__name__) + + +class EndSignal(object): + def __init__(self, errno=0, errmsg=''): + self.errno = errno + self.errmsg = errmsg + + +class ParallelMappedDataset(ProxiedDataset): + """ + Transform samples to mapped samples which is similar to 'basic.MappedDataset', + but multiple workers (threads or processes) will be used + + Notes: + 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[%s], should be ended with 'G' or 'g'" % (args['memsize']) + gb = args['memsize'][:-1] + args['memsize'] = int(gb) * 1024 ** 3 + + self._worker_args = args + self._started = False + self._source = source + self._mapper = mapper + self._exit = False + self._setup() + + def _setup(self): + """setup input/output queues and workers """ + use_process = self._worker_args.get('use_process', False) + 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'] + 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'] + self._inq = Queue(bufsize, memsize=memsize) + self._outq = Queue(bufsize, memsize=memsize) + else: + if six.PY3: + from queue import Queue + else: + from Queue import Queue + from threading import Thread as Worker + from threading import Event + self._inq = Queue(bufsize) + self._outq = Queue(bufsize) + + consumer_num = self._worker_args['worker_num'] + id = str(uuid.uuid4())[-3:] + self._producer = threading.Thread( + target=self._produce, + args=('producer-' + id, self._source, self._inq)) + self._producer.daemon = True + + self._consumers = [] + for i in range(consumer_num): + p = Worker( + target=self._consume, + args=('consumer-' + id + '_' + str(i), self._inq, self._outq, + self._mapper)) + self._consumers.append(p) + p.daemon = True + + self._epoch = -1 + self._feeding_ev = Event() + self._produced = 0 # produced sample in self._produce + self._consumed = 0 # consumed sample in self.next + self._stopped_consumers = 0 + + def _produce(self, id, source, inq): + """Fetch data from source and feed it to 'inq' queue""" + while True: + self._feeding_ev.wait() + if self._exit: + break + try: + inq.put(source.next()) + self._produced += 1 + except StopIteration: + self._feeding_ev.clear() + self._feeding_ev.wait() # wait other guy to wake up me + logger.debug("producer[{}] starts new epoch".format(id)) + except Exception as e: + msg = "producer[{}] failed with error: {}".format(id, str(e)) + inq.put(EndSignal(-1, msg)) + break + + logger.debug("producer[{}] exits".format(id)) + + def _consume(self, id, inq, outq, mapper): + """Fetch data from 'inq', process it and put result to 'outq'""" + while True: + sample = inq.get() + if isinstance(sample, EndSignal): + sample.errmsg += "[consumer[{}] exits]".format(id) + outq.put(sample) + logger.debug("end signal received, " + + "consumer[{}] exits".format(id)) + break + + try: + result = mapper(sample) + outq.put(result) + except Exception as e: + msg = 'failed to map consumer[%s], error: {}'.format(str(e), id) + outq.put(EndSignal(-1, msg)) + break + + def drained(self): + assert self._epoch >= 0, "first epoch has not started yet" + return self._source.drained() and self._produced == self._consumed + + def stop(self): + """ notify to exit + """ + self._exit = True + self._feeding_ev.set() + for _ in range(len(self._consumers)): + self._inq.put(EndSignal(0, "notify consumers to exit")) + + def next(self): + """ get next transformed sample + """ + if self._epoch < 0: + self.reset() + + if self.drained(): + raise StopIteration() + + while True: + sample = self._outq.get() + if isinstance(sample, EndSignal): + self._stopped_consumers += 1 + if sample.errno != 0: + logger.warn("consumer failed with error: {}".format( + sample.errmsg)) + + if self._stopped_consumers < len(self._consumers): + self._inq.put(sample) + else: + raise ValueError("all consumers exited, no more samples") + else: + self._consumed += 1 + return sample + + def reset(self): + """ reset for a new epoch of samples + """ + if self._epoch < 0: + self._epoch = 0 + for p in self._consumers: + p.start() + self._producer.start() + else: + if not self.drained(): + logger.warn("do not reset before epoch[%d] finishes".format( + self._epoch)) + self._produced = self._produced - self._consumed + else: + self._produced = 0 + + self._epoch += 1 + + assert self._stopped_consumers == 0, "some consumers already exited," \ + + " cannot start another epoch" + + self._source.reset() + 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 +def _reader_exit(signum, frame): + logger.debug("Reader process exit.") + sys.exit() + + +signal.signal(signal.SIGTERM, _reader_exit) diff --git a/ppdet/data/transform/post_map.py b/ppdet/data/transform/post_map.py new file mode 100644 index 0000000000000000000000000000000000000000..d556160e5a485753fe2d68600d320fcda6c91496 --- /dev/null +++ b/ppdet/data/transform/post_map.py @@ -0,0 +1,152 @@ +# 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/shared_queue/__init__.py b/ppdet/data/transform/shared_queue/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f118eb76a5f8349162a3a5dfc41bed2bb26a2cb0 --- /dev/null +++ b/ppdet/data/transform/shared_queue/__init__.py @@ -0,0 +1,25 @@ +# 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 +from __future__ import unicode_literals + +__all__ = ['SharedBuffer', 'SharedMemoryMgr', 'SharedQueue'] + +from .sharedmemory import SharedBuffer +from .sharedmemory import SharedMemoryMgr +from .sharedmemory import SharedMemoryError +from .queue import SharedQueue diff --git a/ppdet/data/transform/shared_queue/queue.py b/ppdet/data/transform/shared_queue/queue.py new file mode 100644 index 0000000000000000000000000000000000000000..0bd44d3e9e0b34cefa3adfd8008a90e0963c5b62 --- /dev/null +++ b/ppdet/data/transform/shared_queue/queue.py @@ -0,0 +1,102 @@ +# 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 +from __future__ import unicode_literals + +import sys +import six +if six.PY3: + import pickle + from io import BytesIO as StringIO +else: + import cPickle as pickle + from cStringIO import StringIO + +import logging +import traceback +import multiprocessing as mp +from multiprocessing.queues import Queue +from .sharedmemory import SharedMemoryMgr + +logger = logging.getLogger(__name__) + + +class SharedQueueError(ValueError): + """ SharedQueueError + """ + pass + + +class SharedQueue(Queue): + """ a Queue based on shared memory to communicate data between Process, + and it's interface is compatible with 'multiprocessing.queues.Queue' + """ + + def __init__(self, maxsize=0, mem_mgr=None, memsize=None, pagesize=None): + """ init + """ + if six.PY3: + super(SharedQueue, self).__init__(maxsize, ctx=mp.get_context()) + else: + super(SharedQueue, self).__init__(maxsize) + + if mem_mgr is not None: + self._shared_mem = mem_mgr + else: + self._shared_mem = SharedMemoryMgr( + capacity=memsize, pagesize=pagesize) + + def put(self, obj, **kwargs): + """ put an object to this queue + """ + obj = pickle.dumps(obj, -1) + buff = None + try: + buff = self._shared_mem.malloc(len(obj)) + buff.put(obj) + super(SharedQueue, self).put(buff, **kwargs) + except Exception as e: + stack_info = traceback.format_exc() + err_msg = 'failed to put a element to SharedQueue '\ + 'with stack info[%s]' % (stack_info) + logger.warn(err_msg) + + if buff is not None: + buff.free() + raise e + + def get(self, **kwargs): + """ get an object from this queue + """ + buff = None + try: + buff = super(SharedQueue, self).get(**kwargs) + data = buff.get() + return pickle.load(StringIO(data)) + except Exception as e: + stack_info = traceback.format_exc() + err_msg = 'failed to get element from SharedQueue '\ + 'with stack info[%s]' % (stack_info) + logger.warn(err_msg) + raise e + finally: + if buff is not None: + buff.free() + + def release(self): + self._shared_mem.release() + self._shared_mem = None diff --git a/ppdet/data/transform/shared_queue/sharedmemory.py b/ppdet/data/transform/shared_queue/sharedmemory.py new file mode 100644 index 0000000000000000000000000000000000000000..765708d3b960b61fac8cd2ed985e29f43bab0fe0 --- /dev/null +++ b/ppdet/data/transform/shared_queue/sharedmemory.py @@ -0,0 +1,535 @@ +# 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. + +# utils for memory management which is allocated on sharedmemory, +# note that these structures may not be thread-safe + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import os +import time +import math +import struct +import sys +import six + +if six.PY3: + import pickle +else: + import cPickle as pickle + +import json +import uuid +import random +import numpy as np +import weakref +import logging +from multiprocessing import Lock +from multiprocessing import RawArray + +logger = logging.getLogger(__name__) + + +class SharedMemoryError(ValueError): + """ SharedMemoryError + """ + pass + + +class SharedBufferError(SharedMemoryError): + """ SharedBufferError + """ + pass + + +class MemoryFullError(SharedMemoryError): + """ MemoryFullError + """ + + def __init__(self, errmsg=''): + super(MemoryFullError, self).__init__() + self.errmsg = errmsg + + +def memcopy(dst, src, offset=0, length=None): + """ copy data from 'src' to 'dst' in bytes + """ + length = length if length is not None else len(src) + assert type(dst) == np.ndarray, 'invalid type for "dst" in memcopy' + if type(src) is not np.ndarray: + if type(src) is str and six.PY3: + src = src.encode() + src = np.frombuffer(src, dtype='uint8', count=len(src)) + + dst[:] = src[offset:offset + length] + + +class SharedBuffer(object): + """ Buffer allocated from SharedMemoryMgr, and it stores data on shared memory + + note that: + every instance of this should be freed explicitely by calling 'self.free' + """ + + def __init__(self, owner, capacity, pos, size=0, alloc_status=''): + """ Init + + Args: + owner (str): manager to own this buffer + capacity (int): capacity in bytes for this buffer + pos (int): page position in shared memory + size (int): bytes already used + alloc_status (str): debug info about allocator when allocate this + """ + self._owner = owner + self._cap = capacity + self._pos = pos + self._size = size + self._alloc_status = alloc_status + assert self._pos >= 0 and self._cap > 0, \ + "invalid params[%d:%d] to construct SharedBuffer" \ + % (self._pos, self._cap) + + def owner(self): + """ get owner + """ + return SharedMemoryMgr.get_mgr(self._owner) + + def put(self, data, override=False): + """ put data to this buffer + + Args: + data (str): data to be stored in this buffer + + Returns: + None + + Raises: + SharedMemoryError when not enough space in this buffer + """ + assert type(data) in [str, bytes], \ + 'invalid type[%s] for SharedBuffer::put' % (str(type(data))) + if self._size > 0 and not override: + raise SharedBufferError('already has already been setted before') + + if self.capacity() < len(data): + raise SharedBufferError('data[%d] is larger than size of buffer[%s]'\ + % (len(data), str(self))) + + self.owner().put_data(self, data) + self._size = len(data) + + def get(self, offset=0, size=None, no_copy=True): + """ get the data stored this buffer + + Args: + offset (int): position for the start point to 'get' + size (int): size to get + + Returns: + data (np.ndarray('uint8')): user's data in numpy + which is passed in by 'put' + None: if no data stored in + """ + offset = offset if offset >= 0 else self._size + offset + if self._size <= 0: + return None + + size = self._size if size is None else size + assert offset + size <= self._cap, 'invalid offset[%d] '\ + 'or size[%d] for capacity[%d]' % (offset, size, self._cap) + return self.owner().get_data(self, offset, size, no_copy=no_copy) + + def size(self): + """ bytes of used memory + """ + return self._size + + def resize(self, size): + """ resize the used memory to 'size', should not be greater than capacity + """ + assert size >= 0 and size <= self._cap, \ + "invalid size[%d] for resize" % (size) + + self._size = size + + def capacity(self): + """ size of allocated memory + """ + return self._cap + + def __str__(self): + """ human readable format + """ + return "SharedBuffer(owner:%s, pos:%d, size:%d, "\ + "capacity:%d, alloc_status:[%s], pid:%d)" \ + % (str(self._owner), self._pos, self._size, \ + self._cap, self._alloc_status, os.getpid()) + + def free(self): + """ free this buffer to it's owner + """ + if self._owner is not None: + self.owner().free(self) + self._owner = None + self._cap = 0 + self._pos = -1 + self._size = 0 + return True + else: + return False + + +class PageAllocator(object): + """ allocator used to malloc and free shared memory which + is split into pages + """ + s_allocator_header = 12 + + def __init__(self, base, total_pages, page_size): + """ init + """ + self._magic_num = 1234321000 + random.randint(100, 999) + self._base = base + self._total_pages = total_pages + self._page_size = page_size + + header_pages = int( + math.ceil((total_pages + self.s_allocator_header) / page_size)) + + self._header_pages = header_pages + self._free_pages = total_pages - header_pages + self._header_size = self._header_pages * page_size + self._reset() + + def _dump_alloc_info(self, fname): + hpages, tpages, pos, used = self.header() + + start = self.s_allocator_header + end = start + self._page_size * hpages + alloc_flags = self._base[start:end].tostring() + info = { + 'magic_num': self._magic_num, + 'header_pages': hpages, + 'total_pages': tpages, + 'pos': pos, + 'used': used + } + info['alloc_flags'] = alloc_flags + fname = fname + '.' + str(uuid.uuid4())[:6] + with open(fname, 'wb') as f: + f.write(pickle.dumps(info, -1)) + logger.warn('dump alloc info to file[%s]' % (fname)) + + def _reset(self): + alloc_page_pos = self._header_pages + used_pages = self._header_pages + header_info = struct.pack( + str('III'), self._magic_num, alloc_page_pos, used_pages) + assert len(header_info) == self.s_allocator_header, \ + 'invalid size of header_info' + + memcopy(self._base[0:self.s_allocator_header], header_info) + self.set_page_status(0, self._header_pages, '1') + self.set_page_status(self._header_pages, self._free_pages, '0') + + def header(self): + """ get header info of this allocator + """ + header_str = self._base[0:self.s_allocator_header].tostring() + magic, pos, used = struct.unpack(str('III'), header_str) + + assert magic == self._magic_num, \ + 'invalid header magic[%d] in shared memory' % (magic) + return self._header_pages, self._total_pages, pos, used + + def empty(self): + """ are all allocatable pages available + """ + header_pages, pages, pos, used = self.header() + return header_pages == used + + def full(self): + """ are all allocatable pages used + """ + header_pages, pages, pos, used = self.header() + return header_pages + used == pages + + def __str__(self): + header_pages, pages, pos, used = self.header() + desc = '{page_info[magic:%d,total:%d,used:%d,header:%d,alloc_pos:%d,pagesize:%d]}' \ + % (self._magic_num, pages, used, header_pages, pos, self._page_size) + return 'PageAllocator:%s' % (desc) + + def set_alloc_info(self, alloc_pos, used_pages): + """ set allocating position to new value + """ + memcopy(self._base[4:12], struct.pack(str('II'), alloc_pos, used_pages)) + + def set_page_status(self, start, page_num, status): + """ set pages from 'start' to 'end' with new same status 'status' + """ + assert status in ['0', '1'], 'invalid status[%s] for page status '\ + 'in allocator[%s]' % (status, str(self)) + start += self.s_allocator_header + end = start + page_num + assert start >= 0 and end <= self._header_size, 'invalid end[%d] of pages '\ + 'in allocator[%s]' % (end, str(self)) + memcopy(self._base[start:end], str(status * page_num)) + + def get_page_status(self, start, page_num, ret_flag=False): + start += self.s_allocator_header + end = start + page_num + assert start >= 0 and end <= self._header_size, 'invalid end[%d] of pages '\ + 'in allocator[%s]' % (end, str(self)) + status = self._base[start:end].tostring().decode() + if ret_flag: + return status + + zero_num = status.count('0') + if zero_num == 0: + return (page_num, 1) + else: + return (zero_num, 0) + + def malloc_page(self, page_num): + header_pages, pages, pos, used = self.header() + end = pos + page_num + if end > pages: + pos = self._header_pages + end = pos + page_num + + start_pos = pos + flags = '' + while True: + # maybe flags already has some '0' pages, + # so just check 'page_num - len(flags)' pages + flags = self.get_page_status( + pos, page_num, ret_flag=True) + + if flags.count('0') == page_num: + break + + # not found enough pages, so shift to next few pages + free_pos = flags.rfind('1') + 1 + pos += free_pos + end = pos + page_num + if end > pages: + pos = self._header_pages + end = pos + page_num + flags = '' + + # not found available pages after scan all pages + if pos <= start_pos and end >= start_pos: + logger.debug('not found available pages after scan all pages') + break + + page_status = (flags.count('0'), 0) + if page_status != (page_num, 0): + free_pages = self._total_pages - used + if free_pages == 0: + err_msg = 'all pages have been used:%s' % (str(self)) + else: + err_msg = 'not found available pages with page_status[%s] '\ + 'and %d free pages' % (str(page_status), free_pages) + err_msg = 'failed to malloc %d pages at pos[%d] for reason[%s] and allocator status[%s]' \ + % (page_num, pos, err_msg, str(self)) + raise MemoryFullError(err_msg) + + self.set_page_status(pos, page_num, '1') + used += page_num + self.set_alloc_info(end, used) + return pos + + def free_page(self, start, page_num): + """ free 'page_num' pages start from 'start' + """ + page_status = self.get_page_status(start, page_num) + assert page_status == (page_num, 1), \ + 'invalid status[%s] when free [%d, %d]' \ + % (str(page_status), start, page_num) + self.set_page_status(start, page_num, '0') + _, _, pos, used = self.header() + used -= page_num + self.set_alloc_info(pos, used) + + +DEFAULT_SHARED_MEMORY_SIZE = 1024 * 1024 * 1024 + + +class SharedMemoryMgr(object): + """ manage a continouse block of memory, provide + 'malloc' to allocate new buffer, and 'free' to free buffer + """ + s_memory_mgrs = weakref.WeakValueDictionary() + s_mgr_num = 0 + s_log_statis = False + + @classmethod + def get_mgr(cls, id): + """ get a SharedMemoryMgr with size of 'capacity' + """ + assert id in cls.s_memory_mgrs, 'invalid id[%s] for memory managers' % ( + id) + return cls.s_memory_mgrs[id] + + def __init__(self, capacity=None, pagesize=None): + """ init + """ + logger.debug('create SharedMemoryMgr') + + pagesize = 64 * 1024 if pagesize is None else pagesize + assert type(pagesize) is int, "invalid type of pagesize[%s]" \ + % (str(pagesize)) + + capacity = DEFAULT_SHARED_MEMORY_SIZE if capacity is None else capacity + assert type(capacity) is int, "invalid type of capacity[%s]" \ + % (str(capacity)) + + assert capacity > 0, '"size of shared memory should be greater than 0' + self._released = False + self._cap = capacity + self._page_size = pagesize + + assert self._cap % self._page_size == 0, \ + "capacity[%d] and pagesize[%d] are not consistent" \ + % (self._cap, self._page_size) + self._total_pages = self._cap // self._page_size + + self._pid = os.getpid() + SharedMemoryMgr.s_mgr_num += 1 + self._id = self._pid * 100 + SharedMemoryMgr.s_mgr_num + SharedMemoryMgr.s_memory_mgrs[self._id] = self + self._locker = Lock() + self._setup() + + def _setup(self): + self._shared_mem = RawArray('c', self._cap) + self._base = np.frombuffer( + self._shared_mem, dtype='uint8', count=self._cap) + self._locker.acquire() + try: + self._allocator = PageAllocator(self._base, self._total_pages, + self._page_size) + finally: + self._locker.release() + + def malloc(self, size, wait=True): + """ malloc a new SharedBuffer + + Args: + size (int): buffer size to be malloc + wait (bool): whether to wait when no enough memory + + Returns: + SharedBuffer + + Raises: + SharedMemoryError when not found available memory + """ + page_num = int(math.ceil(size / self._page_size)) + size = page_num * self._page_size + + start = None + ct = 0 + errmsg = '' + while True: + self._locker.acquire() + try: + start = self._allocator.malloc_page(page_num) + alloc_status = str(self._allocator) + except MemoryFullError as e: + start = None + errmsg = e.errmsg + if not wait: + raise e + finally: + self._locker.release() + + if start is None: + time.sleep(0.1) + if ct % 100 == 0: + logger.warn('not enough space for reason[%s]' % (errmsg)) + + ct += 1 + else: + break + + return SharedBuffer(self._id, size, start, alloc_status=alloc_status) + + def free(self, shared_buf): + """ free a SharedBuffer + + Args: + shared_buf (SharedBuffer): buffer to be freed + + Returns: + None + + Raises: + SharedMemoryError when failed to release this buffer + """ + assert shared_buf._owner == self._id, "invalid shared_buf[%s] "\ + "for it's not allocated from me[%s]" % (str(shared_buf), str(self)) + cap = shared_buf.capacity() + start_page = shared_buf._pos + page_num = cap // self._page_size + + #maybe we don't need this lock here + self._locker.acquire() + try: + self._allocator.free_page(start_page, page_num) + finally: + self._locker.release() + + def put_data(self, shared_buf, data): + """ fill 'data' into 'shared_buf' + """ + assert len(data) <= shared_buf.capacity(), 'too large data[%d] '\ + 'for this buffer[%s]' % (len(data), str(shared_buf)) + start = shared_buf._pos * self._page_size + end = start + len(data) + assert start >= 0 and end <= self._cap, "invalid start "\ + "position[%d] when put data to buff:%s" % (start, str(shared_buf)) + self._base[start:end] = np.frombuffer(data, 'uint8', len(data)) + + def get_data(self, shared_buf, offset, size, no_copy=True): + """ extract 'data' from 'shared_buf' in range [offset, offset + size) + """ + start = shared_buf._pos * self._page_size + start += offset + if no_copy: + return self._base[start:start + size] + else: + return self._base[start:start + size].tostring() + + def __str__(self): + return 'SharedMemoryMgr:{id:%d, %s}' % (self._id, str(self._allocator)) + + def __del__(self): + if SharedMemoryMgr.s_log_statis: + logger.info('destroy [%s]' % (self)) + + if not self._released and not self._allocator.empty(): + logger.debug('not empty when delete this SharedMemoryMgr[%s]' % + (self)) + else: + self._released = True + + if self._id in SharedMemoryMgr.s_memory_mgrs: + del SharedMemoryMgr.s_memory_mgrs[self._id] + SharedMemoryMgr.s_mgr_num -= 1 diff --git a/ppdet/data/transform/transformer.py b/ppdet/data/transform/transformer.py new file mode 100644 index 0000000000000000000000000000000000000000..ad8845677db7cc17b5212876762a9b697344ba29 --- /dev/null +++ b/ppdet/data/transform/transformer.py @@ -0,0 +1,108 @@ +# 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/experimental/__init__.py b/ppdet/experimental/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f70396193135830ef2d1de8e357842ea1ef0eea2 --- /dev/null +++ b/ppdet/experimental/__init__.py @@ -0,0 +1,20 @@ +# 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 .mixed_precision import * +from . import mixed_precision + +__all__ = mixed_precision.__all__ diff --git a/ppdet/experimental/mixed_precision.py b/ppdet/experimental/mixed_precision.py new file mode 100644 index 0000000000000000000000000000000000000000..d8c333bf4b99b51857e47779b2f8998758cd0318 --- /dev/null +++ b/ppdet/experimental/mixed_precision.py @@ -0,0 +1,338 @@ +# 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 + +import six +from paddle.fluid.framework import Parameter +from paddle.fluid import layers +from paddle.fluid import core +from paddle.fluid import unique_name +import paddle.fluid.layer_helper_base as lhb +import paddle.fluid.optimizer as optim + +__all__ = ['mixed_precision_global_state', 'mixed_precision_context', + 'StaticLossScale', 'DynamicLossScale'] + +_mixed_precision_global_state = None + + +def mixed_precision_global_state(): + return _mixed_precision_global_state + + +class LossScale(object): + def __init__(self): + super(LossScale, self).__init__() + + def get_loss_scale_var(self): + return self.scale + + def increment(self): + raise NotImplementedError() + + def decrement(self): + raise NotImplementedError() + + +class StaticLossScale(LossScale): + """ + Static (fixed) loss scale manager. + + Args: + init_loss_scale (float): initial loss scale value. + + Examples: + + .. code-block:: python + + from paddle import fluid + from ppdet.experimental import (mixed_precision_context, + StaticLossScale) + + with mixed_precision_context(StaticLossScale(8.), True) as ctx: + # ... + # scale loss + loss_scale = ctx.get_loss_scale_var() + + """ + + def __init__(self, init_loss_scale=1.): + super(StaticLossScale, self).__init__() + self.scale = layers.create_global_var( + name=unique_name.generate("loss_scale"), + shape=[1], + value=init_loss_scale, + dtype='float32', + persistable=True) + + +class DynamicLossScale(LossScale): + """ + Dynamic loss scale manager. it works as follows: + if gradients is valid for `increment_every` steps, loss scale values is + increased by `factor`, otherwise loss scale values is decreased by `factor` + + Args: + init_loss_scale (float): initial loss scale value. + increment_every (int): minimum 'good' steps before loss scale increase. + factor (float): increase/decrease loss scale by this much. + + Examples: + + .. code-block:: python + + from paddle import fluid + from ppdet.experimental import (mixed_precision_context, + DynamicLossScale) + + loss_scale = DynamicLossScale(8., 1000, 4.) + with mixed_precision_context(loss_scale, True) as ctx: + # ... + # scale loss + loss_scale = ctx.get_loss_scale_var() + + """ + + def __init__(self, init_loss_scale=2**15, increment_every=2000, factor=2.): + super(DynamicLossScale, self).__init__() + self.scale = layers.create_global_var( + name=unique_name.generate("loss_scale"), + shape=[1], + value=init_loss_scale, + dtype='float32', + persistable=True) + self.good_steps = layers.create_global_var( + name=unique_name.generate("good_steps"), + shape=[1], + value=0, + dtype='int32', + persistable=True) + self.increment_every = layers.fill_constant( + shape=[1], dtype='int32', value=increment_every) + self.factor = factor + + def increment(self): + enough_steps = layers.less_than(self.increment_every, + self.good_steps + 1) + with layers.Switch() as switch: + with switch.case(enough_steps): + new_scale = self.scale * self.factor + scale_valid = layers.isfinite(new_scale) + with layers.Switch() as switch2: + with switch2.case(scale_valid): + layers.assign(new_scale, self.scale) + layers.assign(layers.zeros_like(self.good_steps), + self.good_steps) + with switch2.default(): + layers.increment(self.good_steps) + with switch.default(): + layers.increment(self.good_steps) + + def decrement(self): + new_scale = self.scale / self.factor + one = layers.fill_constant(shape=[1], dtype='float32', value=1.0) + less_than_one = layers.less_than(new_scale, one) + with layers.Switch() as switch: + with switch.case(less_than_one): + layers.assign(one, self.scale) + with switch.default(): + layers.assign(new_scale, self.scale) + + layers.assign(layers.zeros_like(self.good_steps), + self.good_steps) + + +class mixed_precision_context(object): + """ + Context manager for mixed precision training. + + Args: + loss_scale (float, str or obj): loss scale settings, can be: + 1. an number: use fixed loss scale. + 2. 'dynamic': use a default `DynamicLossScale`. + 3. `DynamicLossScale` or `StaticLossScale` instance. + enabled (bool): enable mixed precision training. + + Examples: + + .. code-block:: python + + from paddle import fluid + from ppdet.experimental import mixed_precision_context + + with mixed_precision_context('dynamic', True) as ctx: + # cast inputs to float16 + inputs = fluid.layers.cast(inputs, "float16") + # build model here + logits = model(inputs) + # use float32 for softmax + logits = fluid.layers.cast(logits, "float32") + softmax = fluid.layers.softmax(logits) + loss = fluid.layers.cross_entropy(input=softmax, label=label) + avg_loss = fluid.layers.mean(loss) + # scale loss + loss_scale = ctx.get_loss_scale_var() + avg_loss *= loss_scale + optimizer = fluid.optimizer.Momentum(...) + optimizer.minimize(avg_loss) + + """ + + def __init__(self, loss_scale=1., enabled=True): + super(mixed_precision_context, self).__init__() + self.enabled = enabled + if not enabled: + return + monkey_patch() + if isinstance(loss_scale, six.integer_types + (float,)): + self.loss_scale = StaticLossScale(loss_scale) + elif loss_scale == 'dynamic': + self.loss_scale = DynamicLossScale() + else: + assert isinstance(loss_scale, LossScale), \ + "Invalid loss scale argument" + self.loss_scale = loss_scale + + @property + def dynamic_scaling(self): + return isinstance(self.loss_scale, DynamicLossScale) + + def __getattr__(self, attr): + if attr in ['get_loss_scale_var', 'increment', 'decrement']: + return getattr(self.loss_scale, attr) + + def __enter__(self): + if not self.enabled: + return + global _mixed_precision_global_state + _mixed_precision_global_state = self + return mixed_precision_global_state() + + def __exit__(self, *args): + if not self.enabled: + return + global _mixed_precision_global_state + _mixed_precision_global_state = None + return mixed_precision_global_state() + + +def create_parameter(self, + attr, + shape, + dtype, + is_bias=False, + default_initializer=None): + mp_state = mixed_precision_global_state() + is_half = (isinstance(dtype, str) and dtype == 'float16') \ + or (isinstance(dtype, core.VarDesc.VarType) + and dtype == core.VarDesc.VarType.FP16) + + if is_half and mp_state is not None: + dtype = 'float32' + + param = self._create_parameter(attr, shape, dtype, + is_bias, default_initializer) + if not is_half or mp_state is None: + return param + + param16 = self.main_program.current_block().create_var( + name=param.name + '.fp16', + dtype='float16', + type=param.type, + persistable=False) + self.append_op( + type='cast', + inputs={'X': [param]}, + outputs={'Out': [param16]}, + attrs={'in_dtype': param.dtype, + 'out_dtype': param16.dtype}) + return param16 + + +def scale_gradient(block, context): + state = mixed_precision_global_state() + if state is None: + return + scale = state.get_loss_scale_var() + op_desc = block.desc.op(block.desc.op_size() - 1) + op_role_attr_name = core.op_proto_and_checker_maker.kOpRoleAttrName() + bwd_role = core.op_proto_and_checker_maker.OpRole.Backward + for name in [n for n in op_desc.output_arg_names() if n in context]: + fwd_var = block._var_recursive(context[name]) + if not isinstance(fwd_var, Parameter): + continue # TODO verify all use cases + clip_op_desc = block.desc.append_op() + clip_op_desc.set_type("elementwise_div") + clip_op_desc.set_input("X", [name]) + clip_op_desc.set_input("Y", [scale.name]) + clip_op_desc.set_output("Out", [name]) + clip_op_desc._set_attr(op_role_attr_name, bwd_role) + + +def update_loss_scale(grads): + state = mixed_precision_global_state() + if state is None or not state.dynamic_scaling: + return + per_grad_check = layers.stack([layers.reduce_sum(g) for g in grads]) + grad_valid = layers.isfinite(per_grad_check) + + with layers.Switch() as switch: + with switch.case(grad_valid): + state.increment() + with switch.default(): + state.decrement() + return grad_valid + + +def backward(self, loss, **kwargs): + state = mixed_precision_global_state() + callbacks = 'callbacks' in kwargs and kwargs['callbacks'] or None + if callbacks is None: + from paddle.fluid.clip import error_clip_callback + callbacks = [error_clip_callback] # XXX what if gradient is zero? + if state is not None: + kwargs['callbacks'] = [scale_gradient] + callbacks + else: + kwargs['callbacks'] = callbacks + param_grads = self._backward(loss, **kwargs) + if state is not None: + grad_valid = update_loss_scale(v for k, v in param_grads) + if state.dynamic_scaling: + with layers.Switch() as switch: + with switch.case(grad_valid): + pass + with switch.default(): + for _, g in param_grads: + layers.assign(layers.zeros_like(g), g) + + return param_grads + + +mixed_precision_patched = False + + +# XXX this is a temporary measure, until thoroughly evaluated +def monkey_patch(): + global mixed_precision_patched + if mixed_precision_patched: + return + create_parameter_orig = lhb.LayerHelperBase.create_parameter + lhb.LayerHelperBase.create_parameter = create_parameter + lhb.LayerHelperBase._create_parameter = create_parameter_orig + backward_orig = optim.Optimizer.backward + optim.Optimizer.backward = backward + optim.Optimizer._backward = backward_orig + mixed_precision_patched = True diff --git a/ppdet/modeling/__init__.py b/ppdet/modeling/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..f9491d771626848a631804ac3bf663f2ee6ec82c --- /dev/null +++ b/ppdet/modeling/__init__.py @@ -0,0 +1,32 @@ +# 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 + +# XXX for triggering decorators +from . import anchor_heads +from . import architectures +from . import backbones +from . import roi_extractors +from . import roi_heads +from . import ops +from . import target_assigners + +from .anchor_heads import * +from .architectures import * +from .backbones import * +from .roi_extractors import * +from .roi_heads import * +from .ops import * +from .target_assigners import * diff --git a/ppdet/modeling/anchor_heads/__init__.py b/ppdet/modeling/anchor_heads/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..1ed22160d23cc5c7437507350037e71134ee8824 --- /dev/null +++ b/ppdet/modeling/anchor_heads/__init__.py @@ -0,0 +1,23 @@ +# 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 . import rpn_head +from . import yolo_head +from . import retina_head + +from .rpn_head import * +from .yolo_head import * +from .retina_head import * diff --git a/ppdet/modeling/anchor_heads/retina_head.py b/ppdet/modeling/anchor_heads/retina_head.py new file mode 100644 index 0000000000000000000000000000000000000000..41246e8b6267881f62386653841ecfb525a254e1 --- /dev/null +++ b/ppdet/modeling/anchor_heads/retina_head.py @@ -0,0 +1,407 @@ +# 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 paddle.fluid as fluid +from paddle.fluid.param_attr import ParamAttr +from paddle.fluid.initializer import Normal, Constant +from paddle.fluid.regularizer import L2Decay +from ppdet.modeling.ops import (AnchorGenerator, RetinaTargetAssign, + RetinaOutputDecoder) + +from ppdet.core.workspace import register + +__all__ = ['RetinaHead'] + + +@register +class RetinaHead(object): + """ + Retina Head + + Args: + anchor_generator (object): `AnchorGenerator` instance + target_assign (object): `RetinaTargetAssign` instance + output_decoder (object): `RetinaOutputDecoder` instance + num_convs_per_octave (int): Number of convolution layers in each octave + num_chan (int): Number of octave output channels + max_level (int): Highest level of FPN output + min_level (int): Lowest level of FPN output + prior_prob (float): Used to set the bias init for the class prediction layer + base_scale (int): Anchors are generated based on this scale + num_scales_per_octave (int): Number of anchor scales per octave + num_classes (int): Number of classes + gamma (float): The parameter in focal loss + alpha (float): The parameter in focal loss + sigma (float): The parameter in smooth l1 loss + """ + __inject__ = ['anchor_generator', 'target_assign', 'output_decoder'] + __shared__ = ['num_classes'] + + def __init__(self, + anchor_generator=AnchorGenerator().__dict__, + target_assign=RetinaTargetAssign().__dict__, + output_decoder=RetinaOutputDecoder().__dict__, + num_convs_per_octave=4, + num_chan=256, + max_level=7, + min_level=3, + prior_prob=0.01, + base_scale=4, + num_scales_per_octave=3, + num_classes=81, + gamma=2.0, + alpha=0.25, + sigma=3.0151134457776365): + self.anchor_generator = anchor_generator + self.target_assign = target_assign + self.output_decoder = output_decoder + self.num_convs_per_octave = num_convs_per_octave + self.num_chan = num_chan + self.max_level = max_level + self.min_level = min_level + self.prior_prob = prior_prob + self.base_scale = base_scale + self.num_scales_per_octave = num_scales_per_octave + self.num_classes = num_classes + self.gamma = gamma + self.alpha = alpha + self.sigma = sigma + if isinstance(anchor_generator, dict): + self.anchor_generator = AnchorGenerator(**anchor_generator) + if isinstance(target_assign, dict): + self.target_assign = RetinaTargetAssign(**target_assign) + if isinstance(output_decoder, dict): + self.output_decoder = RetinaOutputDecoder(**output_decoder) + + def _class_subnet(self, body_feats, spatial_scale): + """ + Get class predictions of all level FPN level. + + Args: + fpn_dict(dict): A dictionary represents the output of FPN with + their name. + spatial_scale(list): A list of multiplicative spatial scale factor. + + Returns: + cls_pred_input(list): Class prediction of all input fpn levels. + """ + assert len(body_feats) == self.max_level - self.min_level + 1 + fpn_name_list = list(body_feats.keys()) + cls_pred_list = [] + for lvl in range(self.min_level, self.max_level + 1): + fpn_name = fpn_name_list[self.max_level - lvl] + subnet_blob = body_feats[fpn_name] + for i in range(self.num_convs_per_octave): + conv_name = 'retnet_cls_conv_n{}_fpn{}'.format(i, lvl) + conv_share_name = 'retnet_cls_conv_n{}_fpn{}'.format( + i, self.min_level) + subnet_blob_in = subnet_blob + subnet_blob = fluid.layers.conv2d( + input=subnet_blob_in, + num_filters=self.num_chan, + filter_size=3, + stride=1, + padding=1, + act='relu', + name=conv_name, + param_attr=ParamAttr( + name=conv_share_name + '_w', + initializer=Normal( + loc=0., scale=0.01)), + bias_attr=ParamAttr( + name=conv_share_name + '_b', + learning_rate=2., + regularizer=L2Decay(0.))) + + # class prediction + cls_name = 'retnet_cls_pred_fpn{}'.format(lvl) + cls_share_name = 'retnet_cls_pred_fpn{}'.format(self.min_level) + num_anchors = self.num_scales_per_octave * len( + self.anchor_generator.aspect_ratios) + cls_dim = num_anchors * (self.num_classes - 1) + # bias initialization: b = -log((1 - pai) / pai) + bias_init = float(-np.log((1 - self.prior_prob) / self.prior_prob)) + out_cls = fluid.layers.conv2d( + input=subnet_blob, + num_filters=cls_dim, + filter_size=3, + stride=1, + padding=1, + act=None, + name=cls_name, + param_attr=ParamAttr( + name=cls_share_name + '_w', + initializer=Normal( + loc=0., scale=0.01)), + bias_attr=ParamAttr( + name=cls_share_name + '_b', + initializer=Constant(value=bias_init), + learning_rate=2., + regularizer=L2Decay(0.))) + cls_pred_list.append(out_cls) + + return cls_pred_list + + def _bbox_subnet(self, body_feats, spatial_scale): + """ + Get bounding box predictions of all level FPN level. + + Args: + fpn_dict(dict): A dictionary represents the output of FPN with + their name. + spatial_scale(list): A list of multiplicative spatial scale factor. + + Returns: + bbox_pred_input(list): Bounding box prediction of all input fpn + levels. + """ + assert len(body_feats) == self.max_level - self.min_level + 1 + fpn_name_list = list(body_feats.keys()) + bbox_pred_list = [] + for lvl in range(self.min_level, self.max_level + 1): + fpn_name = fpn_name_list[self.max_level - lvl] + subnet_blob = body_feats[fpn_name] + for i in range(self.num_convs_per_octave): + conv_name = 'retnet_bbox_conv_n{}_fpn{}'.format(i, lvl) + conv_share_name = 'retnet_bbox_conv_n{}_fpn{}'.format( + i, self.min_level) + subnet_blob_in = subnet_blob + subnet_blob = fluid.layers.conv2d( + input=subnet_blob_in, + num_filters=self.num_chan, + filter_size=3, + stride=1, + padding=1, + act='relu', + name=conv_name, + param_attr=ParamAttr( + name=conv_share_name + '_w', + initializer=Normal( + loc=0., scale=0.01)), + bias_attr=ParamAttr( + name=conv_share_name + '_b', + learning_rate=2., + regularizer=L2Decay(0.))) + + # bbox prediction + bbox_name = 'retnet_bbox_pred_fpn{}'.format(lvl) + bbox_share_name = 'retnet_bbox_pred_fpn{}'.format(self.min_level) + num_anchors = self.num_scales_per_octave * len( + self.anchor_generator.aspect_ratios) + bbox_dim = num_anchors * 4 + out_bbox = fluid.layers.conv2d( + input=subnet_blob, + num_filters=bbox_dim, + filter_size=3, + stride=1, + padding=1, + act=None, + name=bbox_name, + param_attr=ParamAttr( + name=bbox_share_name + '_w', + initializer=Normal( + loc=0., scale=0.01)), + bias_attr=ParamAttr( + name=bbox_share_name + '_b', + learning_rate=2., + regularizer=L2Decay(0.))) + bbox_pred_list.append(out_bbox) + return bbox_pred_list + + def _anchor_generate(self, body_feats, spatial_scale): + """ + Get anchor boxes of all level FPN level. + + Args: + fpn_dict(dict): A dictionary represents the output of FPN with + their name. + spatial_scale(list): A list of multiplicative spatial scale factor. + + Return: + anchor_input(list): Anchors of all input fpn levels with shape of. + anchor_var_input(list): Anchor variance of all input fpn levels with + shape. + """ + assert len(body_feats) == self.max_level - self.min_level + 1 + fpn_name_list = list(body_feats.keys()) + anchor_list = [] + anchor_var_list = [] + for lvl in range(self.min_level, self.max_level + 1): + anchor_sizes = [] + stride = int(1 / spatial_scale[self.max_level - lvl]) + for octave in range(self.num_scales_per_octave): + anchor_size = stride * ( + 2**(float(octave) / + float(self.num_scales_per_octave))) * self.base_scale + anchor_sizes.append(anchor_size) + fpn_name = fpn_name_list[self.max_level - lvl] + anchor, anchor_var = self.anchor_generator( + input=body_feats[fpn_name], + anchor_sizes=anchor_sizes, + aspect_ratios=self.anchor_generator.aspect_ratios, + stride=[stride, stride]) + anchor_list.append(anchor) + anchor_var_list.append(anchor_var) + return anchor_list, anchor_var_list + + def _get_output(self, body_feats, spatial_scale): + """ + Get class, bounding box predictions and anchor boxes of all level FPN level. + + Args: + fpn_dict(dict): A dictionary represents the output of FPN with + their name. + spatial_scale(list): A list of multiplicative spatial scale factor. + + Returns: + cls_pred_input(list): Class prediction of all input fpn levels. + bbox_pred_input(list): Bounding box prediction of all input fpn + levels. + anchor_input(list): Anchors of all input fpn levels with shape of. + anchor_var_input(list): Anchor variance of all input fpn levels with + shape. + """ + assert len(body_feats) == self.max_level - self.min_level + 1 + # class subnet + cls_pred_list = self._class_subnet(body_feats, spatial_scale) + # bbox subnet + bbox_pred_list = self._bbox_subnet(body_feats, spatial_scale) + #generate anchors + anchor_list, anchor_var_list = self._anchor_generate(body_feats, + spatial_scale) + cls_pred_reshape_list = [] + bbox_pred_reshape_list = [] + anchor_reshape_list = [] + anchor_var_reshape_list = [] + for i in range(self.max_level - self.min_level + 1): + cls_pred_transpose = fluid.layers.transpose( + cls_pred_list[i], perm=[0, 2, 3, 1]) + cls_pred_reshape = fluid.layers.reshape( + cls_pred_transpose, shape=(0, -1, self.num_classes - 1)) + bbox_pred_transpose = fluid.layers.transpose( + bbox_pred_list[i], perm=[0, 2, 3, 1]) + bbox_pred_reshape = fluid.layers.reshape( + bbox_pred_transpose, shape=(0, -1, 4)) + anchor_reshape = fluid.layers.reshape(anchor_list[i], shape=(-1, 4)) + anchor_var_reshape = fluid.layers.reshape( + anchor_var_list[i], shape=(-1, 4)) + cls_pred_reshape_list.append(cls_pred_reshape) + bbox_pred_reshape_list.append(bbox_pred_reshape) + anchor_reshape_list.append(anchor_reshape) + anchor_var_reshape_list.append(anchor_var_reshape) + output = {} + output['cls_pred'] = cls_pred_reshape_list + output['bbox_pred'] = bbox_pred_reshape_list + output['anchor'] = anchor_reshape_list + output['anchor_var'] = anchor_var_reshape_list + return output + + def get_prediction(self, body_feats, spatial_scale, im_info): + """ + Get prediction bounding box in test stage. + + Args: + fpn_dict(dict): A dictionary represents the output of FPN with + their name. + spatial_scale(list): A list of multiplicative spatial scale factor. + im_info (Variable): A 2-D LoDTensor with shape [B, 3]. B is the + number of input images, each element consists of im_height, + im_width, im_scale. + + Returns: + pred_result(Variable): Prediction result with shape [N, 6]. Each + row has 6 values: [label, confidence, xmin, ymin, xmax, ymax]. + N is the total number of prediction. + """ + output = self._get_output(body_feats, spatial_scale) + cls_pred_reshape_list = output['cls_pred'] + bbox_pred_reshape_list = output['bbox_pred'] + anchor_reshape_list = output['anchor'] + for i in range(self.max_level - self.min_level + 1): + cls_pred_reshape_list[i] = fluid.layers.sigmoid( + cls_pred_reshape_list[i]) + pred_result = self.output_decoder( + bboxes=bbox_pred_reshape_list, + scores=cls_pred_reshape_list, + anchors=anchor_reshape_list, + im_info=im_info) + return {'bbox': pred_result} + + def get_loss(self, body_feats, spatial_scale, im_info, gt_box, gt_label, + is_crowd): + """ + Calculate the loss of retinanet. + Args: + fpn_dict(dict): A dictionary represents the output of FPN with + their name. + spatial_scale(list): A list of multiplicative spatial scale factor. + im_info(Variable): A 2-D LoDTensor with shape [B, 3]. B is the + number of input images, each element consists of im_height, + im_width, im_scale. + gt_box(Variable): The ground-truth bounding boxes with shape [M, 4]. + M is the number of groundtruth. + gt_label(Variable): The ground-truth labels with shape [M, 1]. + M is the number of groundtruth. + is_crowd(Variable): Indicates groud-truth is crowd or not with + shape [M, 1]. M is the number of groundtruth. + + Returns: + Type: dict + loss_cls(Variable): focal loss. + loss_bbox(Variable): smooth l1 loss. + """ + output = self._get_output(body_feats, spatial_scale) + cls_pred_reshape_list = output['cls_pred'] + bbox_pred_reshape_list = output['bbox_pred'] + anchor_reshape_list = output['anchor'] + anchor_var_reshape_list = output['anchor_var'] + + cls_pred_input = fluid.layers.concat(cls_pred_reshape_list, axis=1) + bbox_pred_input = fluid.layers.concat(bbox_pred_reshape_list, axis=1) + anchor_input = fluid.layers.concat(anchor_reshape_list, axis=0) + anchor_var_input = fluid.layers.concat(anchor_var_reshape_list, axis=0) + score_pred, loc_pred, score_tgt, loc_tgt, bbox_weight, fg_num = \ + self.target_assign( + bbox_pred=bbox_pred_input, + cls_logits=cls_pred_input, + anchor_box=anchor_input, + anchor_var=anchor_var_input, + gt_boxes=gt_box, + gt_labels=gt_label, + is_crowd=is_crowd, + im_info=im_info, + num_classes=self.num_classes - 1) + fg_num = fluid.layers.reduce_sum(fg_num, name='fg_num') + loss_cls = fluid.layers.sigmoid_focal_loss( + x=score_pred, + label=score_tgt, + fg_num=fg_num, + gamma=self.gamma, + alpha=self.alpha) + loss_cls = fluid.layers.reduce_sum(loss_cls, name='loss_cls') + loss_bbox = fluid.layers.smooth_l1( + x=loc_pred, + y=loc_tgt, + sigma=self.sigma, + inside_weight=bbox_weight, + outside_weight=bbox_weight) + loss_bbox = fluid.layers.reduce_sum(loss_bbox, name='loss_bbox') + loss_bbox = loss_bbox / fg_num + return {'loss_cls': loss_cls, 'loss_bbox': loss_bbox} diff --git a/ppdet/modeling/anchor_heads/rpn_head.py b/ppdet/modeling/anchor_heads/rpn_head.py new file mode 100644 index 0000000000000000000000000000000000000000..876aafe36553b31cb1b41fec402949eb5a4c9f4b --- /dev/null +++ b/ppdet/modeling/anchor_heads/rpn_head.py @@ -0,0 +1,497 @@ +# 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 + +from paddle import fluid +from paddle.fluid.param_attr import ParamAttr +from paddle.fluid.initializer import Normal +from paddle.fluid.regularizer import L2Decay + +from ppdet.core.workspace import register +from ppdet.modeling.ops import (AnchorGenerator, RPNTargetAssign, + GenerateProposals) + +__all__ = ['RPNTargetAssign', 'GenerateProposals', 'RPNHead', 'FPNRPNHead'] + + +@register +class RPNHead(object): + """ + RPN Head + + Args: + anchor_generator (object): `AnchorGenerator` instance + rpn_target_assign (object): `RPNTargetAssign` instance + train_proposal (object): `GenerateProposals` instance for training + test_proposal (object): `GenerateProposals` instance for testing + num_classes (int): number of classes in rpn output + """ + __inject__ = [ + 'anchor_generator', 'rpn_target_assign', 'train_proposal', + 'test_proposal' + ] + + def __init__(self, + anchor_generator=AnchorGenerator().__dict__, + rpn_target_assign=RPNTargetAssign().__dict__, + train_proposal=GenerateProposals(12000, 2000).__dict__, + test_proposal=GenerateProposals().__dict__, + num_classes=1): + super(RPNHead, self).__init__() + self.anchor_generator = anchor_generator + self.rpn_target_assign = rpn_target_assign + self.train_proposal = train_proposal + self.test_proposal = test_proposal + self.num_classes = num_classes + if isinstance(anchor_generator, dict): + self.anchor_generator = AnchorGenerator(**anchor_generator) + if isinstance(rpn_target_assign, dict): + self.rpn_target_assign = RPNTargetAssign(**rpn_target_assign) + if isinstance(train_proposal, dict): + self.train_proposal = GenerateProposals(**train_proposal) + if isinstance(test_proposal, dict): + self.test_proposal = GenerateProposals(**test_proposal) + + def _get_output(self, input): + """ + Get anchor and RPN head output. + + Args: + input(Variable): feature map from backbone with shape of [N, C, H, W] + + Returns: + rpn_cls_score(Variable): Output of rpn head with shape of + [N, num_anchors, H, W]. + rpn_bbox_pred(Variable): Output of rpn head with shape of + [N, num_anchors * 4, H, W]. + """ + dim_out = input.shape[1] + rpn_conv = fluid.layers.conv2d( + input=input, + num_filters=dim_out, + filter_size=3, + stride=1, + padding=1, + act='relu', + name='conv_rpn', + param_attr=ParamAttr( + name="conv_rpn_w", initializer=Normal( + loc=0., scale=0.01)), + bias_attr=ParamAttr( + name="conv_rpn_b", learning_rate=2., regularizer=L2Decay(0.))) + # Generate anchors + self.anchor, self.anchor_var = self.anchor_generator(input=rpn_conv) + num_anchor = self.anchor.shape[2] + # Proposal classification scores + self.rpn_cls_score = fluid.layers.conv2d( + rpn_conv, + num_filters=num_anchor * self.num_classes, + filter_size=1, + stride=1, + padding=0, + act=None, + name='rpn_cls_score', + param_attr=ParamAttr( + name="rpn_cls_logits_w", initializer=Normal( + loc=0., scale=0.01)), + bias_attr=ParamAttr( + name="rpn_cls_logits_b", + learning_rate=2., + regularizer=L2Decay(0.))) + # Proposal bbox regression deltas + self.rpn_bbox_pred = fluid.layers.conv2d( + rpn_conv, + num_filters=4 * num_anchor, + filter_size=1, + stride=1, + padding=0, + act=None, + name='rpn_bbox_pred', + param_attr=ParamAttr( + name="rpn_bbox_pred_w", initializer=Normal( + loc=0., scale=0.01)), + bias_attr=ParamAttr( + name="rpn_bbox_pred_b", + learning_rate=2., + regularizer=L2Decay(0.))) + return self.rpn_cls_score, self.rpn_bbox_pred + + def get_proposals(self, body_feats, im_info, mode='train'): + """ + Get proposals according to the output of backbone. + + Args: + body_feats (dict): The dictionary of feature maps from backbone. + im_info(Variable): The information of image with shape [N, 3] with + shape (height, width, scale). + body_feat_names(list): A list of names of feature maps from + backbone. + + Returns: + rpn_rois(Variable): Output proposals with shape of (rois_num, 4). + """ + + # In RPN Heads, only the last feature map of backbone is used. + # And body_feat_names[-1] represents the last level name of backbone. + body_feat = list(body_feats.values())[-1] + rpn_cls_score, rpn_bbox_pred = self._get_output(body_feat) + + if self.num_classes == 1: + rpn_cls_prob = fluid.layers.sigmoid( + rpn_cls_score, name='rpn_cls_prob') + else: + rpn_cls_score = fluid.layers.transpose( + rpn_cls_score, perm=[0, 2, 3, 1]) + rpn_cls_score = fluid.layers.reshape( + rpn_cls_score, shape=(0, 0, 0, -1, self.num_classes)) + rpn_cls_prob_tmp = fluid.layers.softmax( + rpn_cls_score, use_cudnn=False, name='rpn_cls_prob') + rpn_cls_prob_slice = fluid.layers.slice( + rpn_cls_prob_tmp, axes=[4], starts=[1], + ends=[self.num_classes]) + rpn_cls_prob, _ = fluid.layers.topk(rpn_cls_prob_slice, 1) + rpn_cls_prob = fluid.layers.reshape( + rpn_cls_prob, shape=(0, 0, 0, -1)) + rpn_cls_prob = fluid.layers.transpose( + rpn_cls_prob, perm=[0, 3, 1, 2]) + prop_op = self.train_proposal if mode == 'train' else self.test_proposal + rpn_rois, rpn_roi_probs = prop_op( + scores=rpn_cls_prob, + bbox_deltas=rpn_bbox_pred, + im_info=im_info, + anchors=self.anchor, + variances=self.anchor_var) + return rpn_rois + + def _transform_input(self, rpn_cls_score, rpn_bbox_pred, anchor, + anchor_var): + rpn_cls_score = fluid.layers.transpose(rpn_cls_score, perm=[0, 2, 3, 1]) + rpn_bbox_pred = fluid.layers.transpose(rpn_bbox_pred, perm=[0, 2, 3, 1]) + anchor = fluid.layers.reshape(anchor, shape=(-1, 4)) + anchor_var = fluid.layers.reshape(anchor_var, shape=(-1, 4)) + rpn_cls_score = fluid.layers.reshape( + x=rpn_cls_score, shape=(0, -1, self.num_classes)) + rpn_bbox_pred = fluid.layers.reshape(x=rpn_bbox_pred, shape=(0, -1, 4)) + return rpn_cls_score, rpn_bbox_pred, anchor, anchor_var + + def _get_loss_input(self): + for attr in ['rpn_cls_score', 'rpn_bbox_pred', 'anchor', 'anchor_var']: + if not getattr(self, attr, None): + raise ValueError("self.{} should not be None,".format(attr), + "call RPNHead.get_proposals first") + return self._transform_input(self.rpn_cls_score, self.rpn_bbox_pred, + self.anchor, self.anchor_var) + + def get_loss(self, im_info, gt_box, is_crowd, gt_label=None): + """ + Sample proposals and Calculate rpn loss. + + Args: + im_info(Variable): The information of image with shape [N, 3] with + shape (height, width, scale). + gt_box(Variable): The ground-truth bounding boxes with shape [M, 4]. + M is the number of groundtruth. + is_crowd(Variable): Indicates groud-truth is crowd or not with + shape [M, 1]. M is the number of groundtruth. + + Returns: + Type: dict + rpn_cls_loss(Variable): RPN classification loss. + rpn_bbox_loss(Variable): RPN bounding box regression loss. + + """ + rpn_cls, rpn_bbox, anchor, anchor_var = self._get_loss_input() + if self.num_classes == 1: + score_pred, loc_pred, score_tgt, loc_tgt, bbox_weight = \ + self.rpn_target_assign( + bbox_pred=rpn_bbox, + cls_logits=rpn_cls, + anchor_box=anchor, + anchor_var=anchor_var, + gt_boxes=gt_box, + is_crowd=is_crowd, + im_info=im_info) + score_tgt = fluid.layers.cast(x=score_tgt, dtype='float32') + score_tgt.stop_gradient = True + rpn_cls_loss = fluid.layers.sigmoid_cross_entropy_with_logits( + x=score_pred, label=score_tgt) + else: + score_pred, loc_pred, score_tgt, loc_tgt, bbox_weight = \ + self.rpn_target_assign( + bbox_pred=rpn_bbox, + cls_logits=rpn_cls, + anchor_box=anchor, + anchor_var=anchor_var, + gt_boxes=gt_box, + gt_labels=gt_label, + is_crowd=is_crowd, + num_classes=self.num_classes, + im_info=im_info) + labels_int64 = fluid.layers.cast(x=score_tgt, dtype='int64') + labels_int64.stop_gradient = True + rpn_cls_loss = fluid.layers.softmax_with_cross_entropy( + logits=score_pred, label=labels_int64, numeric_stable_mode=True) + + rpn_cls_loss = fluid.layers.reduce_mean( + rpn_cls_loss, name='loss_rpn_cls') + + loc_tgt = fluid.layers.cast(x=loc_tgt, dtype='float32') + loc_tgt.stop_gradient = True + rpn_reg_loss = fluid.layers.smooth_l1( + x=loc_pred, + y=loc_tgt, + sigma=3.0, + inside_weight=bbox_weight, + outside_weight=bbox_weight) + rpn_reg_loss = fluid.layers.reduce_sum( + rpn_reg_loss, name='loss_rpn_bbox') + score_shape = fluid.layers.shape(score_tgt) + score_shape = fluid.layers.cast(x=score_shape, dtype='float32') + norm = fluid.layers.reduce_prod(score_shape) + norm.stop_gradient = True + rpn_reg_loss = rpn_reg_loss / norm + + return {'loss_rpn_cls': rpn_cls_loss, 'loss_rpn_bbox': rpn_reg_loss} + + +@register +class FPNRPNHead(RPNHead): + """ + RPN Head that supports FPN input + + Args: + anchor_generator (object): `AnchorGenerator` instance + rpn_target_assign (object): `RPNTargetAssign` instance + train_proposal (object): `GenerateProposals` instance for training + test_proposal (object): `GenerateProposals` instance for testing + anchor_start_size (int): size of anchor at the first scale + num_chan (int): number of FPN output channels + min_level (int): lowest level of FPN output + max_level (int): highest level of FPN output + num_classes (int): number of classes in rpn output + """ + + __inject__ = [ + 'anchor_generator', 'rpn_target_assign', 'train_proposal', + 'test_proposal' + ] + + def __init__(self, + anchor_generator=AnchorGenerator().__dict__, + rpn_target_assign=RPNTargetAssign().__dict__, + train_proposal=GenerateProposals(12000, 2000).__dict__, + test_proposal=GenerateProposals().__dict__, + anchor_start_size=32, + num_chan=256, + min_level=2, + max_level=6, + num_classes=1): + super(FPNRPNHead, self).__init__(anchor_generator, rpn_target_assign, + train_proposal, test_proposal) + self.anchor_start_size = anchor_start_size + self.num_chan = num_chan + self.min_level = min_level + self.max_level = max_level + self.num_classes = num_classes + + self.fpn_rpn_list = [] + self.anchors_list = [] + self.anchor_var_list = [] + + def _get_output(self, input, feat_lvl): + """ + Get anchor and FPN RPN head output at one level. + + Args: + input(Variable): Body feature from backbone. + feat_lvl(int): Indicate the level of rpn output corresponding + to the level of feature map. + + Return: + rpn_cls_score(Variable): Output of one level of fpn rpn head with + shape of [N, num_anchors, H, W]. + rpn_bbox_pred(Variable): Output of one level of fpn rpn head with + shape of [N, num_anchors * 4, H, W]. + """ + slvl = str(feat_lvl) + conv_name = 'conv_rpn_fpn' + slvl + cls_name = 'rpn_cls_logits_fpn' + slvl + bbox_name = 'rpn_bbox_pred_fpn' + slvl + conv_share_name = 'conv_rpn_fpn' + str(self.min_level) + cls_share_name = 'rpn_cls_logits_fpn' + str(self.min_level) + bbox_share_name = 'rpn_bbox_pred_fpn' + str(self.min_level) + + num_anchors = len(self.anchor_generator.aspect_ratios) + conv_rpn_fpn = fluid.layers.conv2d( + input=input, + num_filters=self.num_chan, + filter_size=3, + padding=1, + act='relu', + name=conv_name, + param_attr=ParamAttr( + name=conv_share_name + '_w', + initializer=Normal( + loc=0., scale=0.01)), + bias_attr=ParamAttr( + name=conv_share_name + '_b', + learning_rate=2., + regularizer=L2Decay(0.))) + + self.anchors, self.anchor_var = self.anchor_generator( + input=conv_rpn_fpn, + anchor_sizes=(self.anchor_start_size * 2. + **(feat_lvl - self.min_level), ), + stride=(2.**feat_lvl, 2.**feat_lvl)) + + cls_num_filters = num_anchors * self.num_classes + self.rpn_cls_score = fluid.layers.conv2d( + input=conv_rpn_fpn, + num_filters=cls_num_filters, + filter_size=1, + act=None, + name=cls_name, + param_attr=ParamAttr( + name=cls_share_name + '_w', + initializer=Normal( + loc=0., scale=0.01)), + bias_attr=ParamAttr( + name=cls_share_name + '_b', + learning_rate=2., + regularizer=L2Decay(0.))) + self.rpn_bbox_pred = fluid.layers.conv2d( + input=conv_rpn_fpn, + num_filters=num_anchors * 4, + filter_size=1, + act=None, + name=bbox_name, + param_attr=ParamAttr( + name=bbox_share_name + '_w', + initializer=Normal( + loc=0., scale=0.01)), + bias_attr=ParamAttr( + name=bbox_share_name + '_b', + learning_rate=2., + regularizer=L2Decay(0.))) + return self.rpn_cls_score, self.rpn_bbox_pred + + def _get_single_proposals(self, body_feat, im_info, feat_lvl, mode='train'): + """ + Get proposals in one level according to the output of fpn rpn head + + Args: + body_feat(Variable): the feature map from backone. + im_info(Variable): The information of image with shape [N, 3] with + format (height, width, scale). + feat_lvl(int): Indicate the level of proposals corresponding to + the feature maps. + + Returns: + rpn_rois_fpn(Variable): Output proposals with shape of (rois_num, 4). + rpn_roi_probs_fpn(Variable): Scores of proposals with + shape of (rois_num, 1). + """ + + rpn_cls_score_fpn, rpn_bbox_pred_fpn = self._get_output(body_feat, + feat_lvl) + + prop_op = self.train_proposal if mode == 'train' else self.test_proposal + if self.num_classes == 1: + rpn_cls_prob_fpn = fluid.layers.sigmoid( + rpn_cls_score_fpn, name='rpn_cls_prob_fpn' + str(feat_lvl)) + else: + rpn_cls_score_fpn = fluid.layers.transpose( + rpn_cls_score_fpn, perm=[0, 2, 3, 1]) + rpn_cls_score_fpn = fluid.layers.reshape( + rpn_cls_score_fpn, shape=(0, 0, 0, -1, self.num_classes)) + rpn_cls_prob_fpn = fluid.layers.softmax( + rpn_cls_score_fpn, + use_cudnn=False, + name='rpn_cls_prob_fpn' + str(feat_lvl)) + rpn_cls_prob_fpn = fluid.layers.slice( + rpn_cls_prob_fpn, axes=[4], starts=[1], + ends=[self.num_classes]) + rpn_cls_prob_fpn, _ = fluid.layers.topk(rpn_cls_prob_fpn, 1) + rpn_cls_prob_fpn = fluid.layers.reshape( + rpn_cls_prob_fpn, shape=(0, 0, 0, -1)) + rpn_cls_prob_fpn = fluid.layers.transpose( + rpn_cls_prob_fpn, perm=[0, 3, 1, 2]) + rpn_rois_fpn, rpn_roi_prob_fpn = prop_op( + scores=rpn_cls_prob_fpn, + bbox_deltas=rpn_bbox_pred_fpn, + im_info=im_info, + anchors=self.anchors, + variances=self.anchor_var) + return rpn_rois_fpn, rpn_roi_prob_fpn + + def get_proposals(self, fpn_feats, im_info, mode='train'): + """ + Get proposals in multiple levels according to the output of fpn + rpn head + + Args: + fpn_feats(dict): A dictionary represents the output feature map + of FPN with their name. + im_info(Variable): The information of image with shape [N, 3] with + format (height, width, scale). + + Return: + rois_list(Variable): Output proposals in shape of [rois_num, 4] + """ + rois_list = [] + roi_probs_list = [] + fpn_feat_names = list(fpn_feats.keys()) + for lvl in range(self.min_level, self.max_level + 1): + fpn_feat_name = fpn_feat_names[self.max_level - lvl] + fpn_feat = fpn_feats[fpn_feat_name] + rois_fpn, roi_probs_fpn = self._get_single_proposals( + fpn_feat, im_info, lvl, mode) + self.fpn_rpn_list.append((self.rpn_cls_score, self.rpn_bbox_pred)) + rois_list.append(rois_fpn) + roi_probs_list.append(roi_probs_fpn) + self.anchors_list.append(self.anchors) + self.anchor_var_list.append(self.anchor_var) + prop_op = self.train_proposal if mode == 'train' else self.test_proposal + post_nms_top_n = prop_op.post_nms_top_n + rois_collect = fluid.layers.collect_fpn_proposals( + rois_list, + roi_probs_list, + self.min_level, + self.max_level, + post_nms_top_n, + name='collect') + return rois_collect + + def _get_loss_input(self): + rpn_clses = [] + rpn_bboxes = [] + anchors = [] + anchor_vars = [] + for i in range(len(self.fpn_rpn_list)): + single_input = self._transform_input( + self.fpn_rpn_list[i][0], self.fpn_rpn_list[i][1], + self.anchors_list[i], self.anchor_var_list[i]) + rpn_clses.append(single_input[0]) + rpn_bboxes.append(single_input[1]) + anchors.append(single_input[2]) + anchor_vars.append(single_input[3]) + + rpn_cls = fluid.layers.concat(rpn_clses, axis=1) + rpn_bbox = fluid.layers.concat(rpn_bboxes, axis=1) + anchors = fluid.layers.concat(anchors) + anchor_var = fluid.layers.concat(anchor_vars) + return rpn_cls, rpn_bbox, anchors, anchor_var diff --git a/ppdet/modeling/anchor_heads/yolo_head.py b/ppdet/modeling/anchor_heads/yolo_head.py new file mode 100644 index 0000000000000000000000000000000000000000..7e756f267762827b3666e8143dce9a695fc526e2 --- /dev/null +++ b/ppdet/modeling/anchor_heads/yolo_head.py @@ -0,0 +1,309 @@ +# 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 + +from paddle import fluid +from paddle.fluid.param_attr import ParamAttr +from paddle.fluid.regularizer import L2Decay + +from ppdet.modeling.ops import MultiClassNMS +from ppdet.core.workspace import register + +__all__ = ['YOLOv3Head'] + + +@register +class YOLOv3Head(object): + """ + Head block for YOLOv3 network + + Args: + norm_decay (float): weight decay for normalization layer weights + num_classes (int): number of output classes + ignore_thresh (float): threshold to ignore confidence loss + label_smooth (bool): whether to use label smoothing + anchors (list): anchors + anchor_masks (list): anchor masks + nms (object): an instance of `MultiClassNMS` + """ + __inject__ = ['nms'] + __shared__ = ['num_classes', 'weight_prefix_name'] + + def __init__(self, + norm_decay=0., + num_classes=80, + ignore_thresh=0.7, + label_smooth=True, + anchors=[[10, 13], [16, 30], [33, 23], [30, 61], [62, 45], + [59, 119], [116, 90], [156, 198], [373, 326]], + anchor_masks=[[6, 7, 8], [3, 4, 5], [0, 1, 2]], + nms=MultiClassNMS( + score_threshold=0.01, + nms_top_k=1000, + keep_top_k=100, + nms_threshold=0.45, + background_label=-1).__dict__, + weight_prefix_name=''): + self.norm_decay = norm_decay + self.num_classes = num_classes + self.ignore_thresh = ignore_thresh + self.label_smooth = label_smooth + self.anchor_masks = anchor_masks + self._parse_anchors(anchors) + self.nms = nms + self.prefix_name = weight_prefix_name + if isinstance(nms, dict): + self.nms = MultiClassNMS(**nms) + + def _conv_bn(self, + input, + ch_out, + filter_size, + stride, + padding, + act='leaky', + is_test=True, + name=None): + conv = fluid.layers.conv2d( + input=input, + num_filters=ch_out, + filter_size=filter_size, + stride=stride, + padding=padding, + act=None, + param_attr=ParamAttr(name=name + ".conv.weights"), + bias_attr=False) + + bn_name = name + ".bn" + bn_param_attr = ParamAttr( + regularizer=L2Decay(self.norm_decay), name=bn_name + '.scale') + bn_bias_attr = ParamAttr( + regularizer=L2Decay(self.norm_decay), name=bn_name + '.offset') + out = fluid.layers.batch_norm( + input=conv, + act=None, + is_test=is_test, + param_attr=bn_param_attr, + bias_attr=bn_bias_attr, + moving_mean_name=bn_name + '.mean', + moving_variance_name=bn_name + '.var') + + if act == 'leaky': + out = fluid.layers.leaky_relu(x=out, alpha=0.1) + return out + + def _detection_block(self, input, channel, is_test=True, name=None): + assert channel % 2 == 0, \ + "channel {} cannot be divided by 2 in detection block {}" \ + .format(channel, name) + + conv = input + for j in range(2): + conv = self._conv_bn( + conv, + channel, + filter_size=1, + stride=1, + padding=0, + is_test=is_test, + name='{}.{}.0'.format(name, j)) + conv = self._conv_bn( + conv, + channel * 2, + filter_size=3, + stride=1, + padding=1, + is_test=is_test, + name='{}.{}.1'.format(name, j)) + route = self._conv_bn( + conv, + channel, + filter_size=1, + stride=1, + padding=0, + is_test=is_test, + name='{}.2'.format(name)) + tip = self._conv_bn( + route, + channel * 2, + filter_size=3, + stride=1, + padding=1, + is_test=is_test, + name='{}.tip'.format(name)) + return route, tip + + def _upsample(self, input, scale=2, name=None): + out = fluid.layers.resize_nearest( + input=input, scale=float(scale), name=name) + return out + + def _parse_anchors(self, anchors): + """ + Check ANCHORS/ANCHOR_MASKS in config and parse mask_anchors + + """ + self.anchors = [] + self.mask_anchors = [] + + assert len(anchors) > 0, "ANCHORS not set." + assert len(self.anchor_masks) > 0, "ANCHOR_MASKS not set." + + for anchor in anchors: + assert len(anchor) == 2, "anchor {} len should be 2".format(anchor) + self.anchors.extend(anchor) + + anchor_num = len(anchors) + for masks in self.anchor_masks: + self.mask_anchors.append([]) + for mask in masks: + assert mask < anchor_num, "anchor mask index overflow" + self.mask_anchors[-1].extend(anchors[mask]) + + def _get_outputs(self, input, is_train=True): + """ + Get YOLOv3 head output + + Args: + input (list): List of Variables, output of backbone stages + is_train (bool): whether in train or test mode + + Returns: + outputs (list): Variables of each output layer + """ + + outputs = [] + + # get last out_layer_num blocks in reverse order + out_layer_num = len(self.anchor_masks) + blocks = input[-1:-out_layer_num - 1:-1] + + route = None + for i, block in enumerate(blocks): + if i > 0: # perform concat in first 2 detection_block + block = fluid.layers.concat(input=[route, block], axis=1) + route, tip = self._detection_block( + block, + channel=512 // (2**i), + is_test=(not is_train), + name=self.prefix_name + "yolo_block.{}".format(i)) + + # out channel number = mask_num * (5 + class_num) + num_filters = len(self.anchor_masks[i]) * (self.num_classes + 5) + block_out = fluid.layers.conv2d( + input=tip, + num_filters=num_filters, + filter_size=1, + stride=1, + padding=0, + act=None, + param_attr=ParamAttr(name=self.prefix_name + + "yolo_output.{}.conv.weights".format(i)), + bias_attr=ParamAttr( + regularizer=L2Decay(0.), + name=self.prefix_name + + "yolo_output.{}.conv.bias".format(i))) + outputs.append(block_out) + + if i < len(blocks) - 1: + # do not perform upsample in the last detection_block + route = self._conv_bn( + input=route, + ch_out=256 // (2**i), + filter_size=1, + stride=1, + padding=0, + is_test=(not is_train), + name=self.prefix_name + "yolo_transition.{}".format(i)) + # upsample + route = self._upsample(route) + + return outputs + + def get_loss(self, input, gt_box, gt_label, gt_score): + """ + Get final loss of network of YOLOv3. + + Args: + input (list): List of Variables, output of backbone stages + gt_box (Variable): The ground-truth boudding boxes. + gt_label (Variable): The ground-truth class labels. + gt_score (Variable): The ground-truth boudding boxes mixup scores. + + Returns: + loss (Variable): The loss Variable of YOLOv3 network. + + """ + outputs = self._get_outputs(input, is_train=True) + + losses = [] + downsample = 32 + for i, output in enumerate(outputs): + anchor_mask = self.anchor_masks[i] + loss = fluid.layers.yolov3_loss( + x=output, + gt_box=gt_box, + gt_label=gt_label, + gt_score=gt_score, + anchors=self.anchors, + anchor_mask=anchor_mask, + class_num=self.num_classes, + ignore_thresh=self.ignore_thresh, + downsample_ratio=downsample, + use_label_smooth=self.label_smooth, + name=self.prefix_name + "yolo_loss" + str(i)) + losses.append(fluid.layers.reduce_mean(loss)) + downsample //= 2 + + return sum(losses) + + def get_prediction(self, input, im_size): + """ + Get prediction result of YOLOv3 network + + Args: + input (list): List of Variables, output of backbone stages + im_size (Variable): Variable of size([h, w]) of each image + + Returns: + pred (Variable): The prediction result after non-max suppress. + + """ + + outputs = self._get_outputs(input, is_train=False) + + boxes = [] + scores = [] + downsample = 32 + for i, output in enumerate(outputs): + box, score = fluid.layers.yolo_box( + x=output, + img_size=im_size, + anchors=self.mask_anchors[i], + class_num=self.num_classes, + conf_thresh=self.nms.score_threshold, + downsample_ratio=downsample, + name=self.prefix_name + "yolo_box" + str(i)) + boxes.append(box) + scores.append(fluid.layers.transpose(score, perm=[0, 2, 1])) + + downsample //= 2 + + yolo_boxes = fluid.layers.concat(boxes, axis=1) + yolo_scores = fluid.layers.concat(scores, axis=2) + pred = self.nms(bboxes=yolo_boxes, scores=yolo_scores) + return {'bbox': pred} diff --git a/ppdet/modeling/architectures/__init__.py b/ppdet/modeling/architectures/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..6d2f8aef6172f37e1389a573aa12c4bcc4f71b6c --- /dev/null +++ b/ppdet/modeling/architectures/__init__.py @@ -0,0 +1,35 @@ +# 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 . import faster_rcnn +from . import mask_rcnn +from . import cascade_rcnn +from . import cascade_mask_rcnn +from . import yolov3 +from . import ssd +from . import retinanet +from . import blazeface +from . import faceboxes + +from .faster_rcnn import * +from .mask_rcnn import * +from .cascade_rcnn import * +from .cascade_mask_rcnn import * +from .yolov3 import * +from .ssd import * +from .retinanet import * +from .blazeface import * +from .faceboxes import * diff --git a/ppdet/modeling/architectures/blazeface.py b/ppdet/modeling/architectures/blazeface.py new file mode 100644 index 0000000000000000000000000000000000000000..cc9a2bb338fd1f3b40be09b7e351c24df06651a4 --- /dev/null +++ b/ppdet/modeling/architectures/blazeface.py @@ -0,0 +1,182 @@ +# 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 +from paddle import fluid + +from paddle.fluid.param_attr import ParamAttr +from paddle.fluid.regularizer import L2Decay + +from ppdet.core.workspace import register +from ppdet.modeling.ops import SSDOutputDecoder + +__all__ = ['BlazeFace'] + + +@register +class BlazeFace(object): + """ + BlazeFace: Sub-millisecond Neural Face Detection on Mobile GPUs, + see https://arxiv.org/abs/1907.05047 + + Args: + backbone (object): backbone instance + output_decoder (object): `SSDOutputDecoder` instance + min_sizes (list|None): min sizes of generated prior boxes. + max_sizes (list|None): max sizes of generated prior boxes. Default: None. + num_classes (int): number of output classes + use_density_prior_box (bool): whether or not use density_prior_box + instead of prior_box + densities (list|None): the densities of generated density prior boxes, + this attribute should be a list or tuple of integers + """ + + __category__ = 'architecture' + __inject__ = ['backbone', 'output_decoder'] + __shared__ = ['num_classes'] + + def __init__(self, + backbone="BlazeNet", + output_decoder=SSDOutputDecoder().__dict__, + min_sizes=[[16., 24.], [32., 48., 64., 80., 96., 128.]], + max_sizes=None, + steps=[8., 16.], + num_classes=2, + use_density_prior_box=False, + densities=[[2, 2], [2, 1, 1, 1, 1, 1]]): + super(BlazeFace, self).__init__() + self.backbone = backbone + self.num_classes = num_classes + self.output_decoder = output_decoder + if isinstance(output_decoder, dict): + self.output_decoder = SSDOutputDecoder(**output_decoder) + self.min_sizes = min_sizes + self.max_sizes = max_sizes + self.steps = steps + self.use_density_prior_box = use_density_prior_box + self.densities = densities + + 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'] + + body_feats = self.backbone(im) + locs, confs, box, box_var = self._multi_box_head( + inputs=body_feats, + image=im, + num_classes=self.num_classes, + use_density_prior_box=self.use_density_prior_box) + + if mode == 'train': + loss = fluid.layers.ssd_loss( + locs, + confs, + gt_box, + gt_label, + box, + box_var, + overlap_threshold=0.35, + neg_overlap=0.35) + loss = fluid.layers.reduce_sum(loss) + loss.persistable = True + return {'loss': loss} + else: + pred = self.output_decoder(locs, confs, box, box_var) + return {'bbox': pred} + + def _multi_box_head(self, + inputs, + image, + num_classes=2, + use_density_prior_box=False): + def permute_and_reshape(input, last_dim): + trans = fluid.layers.transpose(input, perm=[0, 2, 3, 1]) + compile_shape = [0, -1, last_dim] + return fluid.layers.reshape(trans, shape=compile_shape) + + def _is_list_or_tuple_(data): + return (isinstance(data, list) or isinstance(data, tuple)) + + locs, confs = [], [] + boxes, vars = [], [] + b_attr = ParamAttr(learning_rate=2., regularizer=L2Decay(0.)) + + for i, input in enumerate(inputs): + min_size = self.min_sizes[i] + + if use_density_prior_box: + densities = self.densities[i] + box, var = fluid.layers.density_prior_box( + input, + image, + densities=densities, + fixed_sizes=min_size, + fixed_ratios=[1.], + clip=False, + offset=0.5) + else: + box, var = fluid.layers.prior_box( + input, + image, + min_sizes=min_size, + max_sizes=None, + steps=[self.steps[i]] * 2, + aspect_ratios=[1.], + clip=False, + flip=False, + offset=0.5) + + num_boxes = box.shape[2] + + box = fluid.layers.reshape(box, shape=[-1, 4]) + var = fluid.layers.reshape(var, shape=[-1, 4]) + num_loc_output = num_boxes * 4 + num_conf_output = num_boxes * num_classes + # get loc + mbox_loc = fluid.layers.conv2d( + input, num_loc_output, 3, 1, 1, bias_attr=b_attr) + loc = permute_and_reshape(mbox_loc, 4) + # get conf + mbox_conf = fluid.layers.conv2d( + input, num_conf_output, 3, 1, 1, bias_attr=b_attr) + conf = permute_and_reshape(mbox_conf, 2) + + locs.append(loc) + confs.append(conf) + boxes.append(box) + vars.append(var) + + face_mbox_loc = fluid.layers.concat(locs, axis=1) + face_mbox_conf = fluid.layers.concat(confs, axis=1) + prior_boxes = fluid.layers.concat(boxes) + box_vars = fluid.layers.concat(vars) + return face_mbox_loc, face_mbox_conf, prior_boxes, box_vars + + def train(self, feed_vars): + return self.build(feed_vars, 'train') + + def eval(self, feed_vars): + return self.build(feed_vars, 'eval') + + def test(self, feed_vars): + return self.build(feed_vars, 'test') + + def is_bbox_normalized(self): + return True diff --git a/ppdet/modeling/architectures/cascade_mask_rcnn.py b/ppdet/modeling/architectures/cascade_mask_rcnn.py new file mode 100644 index 0000000000000000000000000000000000000000..f77ee62759bd9fe7e2b97669e0bdd82d1bddd9ad --- /dev/null +++ b/ppdet/modeling/architectures/cascade_mask_rcnn.py @@ -0,0 +1,384 @@ +# 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 + +from collections import OrderedDict + +import paddle.fluid as fluid + +from ppdet.experimental import mixed_precision_global_state +from ppdet.core.workspace import register + +__all__ = ['CascadeMaskRCNN'] + + +@register +class CascadeMaskRCNN(object): + """ + Cascade Mask R-CNN architecture, see https://arxiv.org/abs/1712.00726 + + Args: + backbone (object): backbone instance + rpn_head (object): `RPNhead` instance + bbox_assigner (object): `BBoxAssigner` instance + roi_extractor (object): ROI extractor instance + bbox_head (object): `BBoxHead` instance + mask_assigner (object): `MaskAssigner` instance + mask_head (object): `MaskHead` instance + fpn (object): feature pyramid network instance + """ + + __category__ = 'architecture' + __inject__ = [ + 'backbone', 'rpn_head', 'bbox_assigner', 'roi_extractor', 'bbox_head', + 'mask_assigner', 'mask_head', 'fpn' + ] + + def __init__(self, + backbone, + rpn_head, + roi_extractor='FPNRoIAlign', + bbox_head='CascadeBBoxHead', + bbox_assigner='CascadeBBoxAssigner', + mask_assigner='MaskAssigner', + mask_head='MaskHead', + rpn_only=False, + fpn='FPN'): + super(CascadeMaskRCNN, self).__init__() + assert fpn is not None, "cascade RCNN requires FPN" + self.backbone = backbone + self.fpn = fpn + self.rpn_head = rpn_head + self.bbox_assigner = bbox_assigner + self.roi_extractor = roi_extractor + self.bbox_head = bbox_head + self.mask_assigner = mask_assigner + self.mask_head = mask_head + self.rpn_only = rpn_only + # Cascade local cfg + self.cls_agnostic_bbox_reg = 2 + (brw0, brw1, brw2) = self.bbox_assigner.bbox_reg_weights + self.cascade_bbox_reg_weights = [ + [1. / brw0, 1. / brw0, 2. / brw0, 2. / brw0], + [1. / brw1, 1. / brw1, 2. / brw1, 2. / brw1], + [1. / brw2, 1. / brw2, 2. / brw2, 2. / brw2] + ] + self.cascade_rcnn_loss_weight = [1.0, 0.5, 0.25] + + def build(self, feed_vars, mode='train'): + if mode == 'train': + required_fields = [ + 'gt_label', 'gt_box', 'gt_mask', 'is_crowd', 'im_info' + ] + else: + required_fields = ['im_shape', 'im_info'] + self._input_check(required_fields, feed_vars) + + im = feed_vars['image'] + if mode == 'train': + gt_box = feed_vars['gt_box'] + is_crowd = feed_vars['is_crowd'] + + im_info = feed_vars['im_info'] + + mixed_precision_enabled = mixed_precision_global_state() is not None + # cast inputs to FP16 + if mixed_precision_enabled: + im = fluid.layers.cast(im, 'float16') + + # backbone + body_feats = self.backbone(im) + + # cast features back to FP32 + if mixed_precision_enabled: + body_feats = OrderedDict((k, fluid.layers.cast(v, 'float32')) + for k, v in body_feats.items()) + + # FPN + if self.fpn is not None: + body_feats, spatial_scale = self.fpn.get_output(body_feats) + + # rpn proposals + 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) + else: + if self.rpn_only: + im_scale = fluid.layers.slice( + im_info, [1], starts=[2], ends=[3]) + im_scale = fluid.layers.sequence_expand(im_scale, rpn_rois) + rois = rpn_rois / im_scale + return {'proposal': rois} + + proposal_list = [] + roi_feat_list = [] + rcnn_pred_list = [] + rcnn_target_list = [] + + proposals = None + bbox_pred = None + for i in range(3): + if i > 0: + refined_bbox = self._decode_box( + proposals, + bbox_pred, + curr_stage=i - 1, ) + else: + refined_bbox = rpn_rois + + if mode == 'train': + outs = self.bbox_assigner( + input_rois=refined_bbox, feed_vars=feed_vars, curr_stage=i) + + proposals = outs[0] + rcnn_target_list.append(outs) + else: + proposals = refined_bbox + proposal_list.append(proposals) + + # extract roi features + roi_feat = self.roi_extractor(body_feats, proposals, spatial_scale) + roi_feat_list.append(roi_feat) + + # bbox head + cls_score, bbox_pred = self.bbox_head.get_output( + roi_feat, + wb_scalar=1.0 / self.cascade_rcnn_loss_weight[i], + name='_' + str(i + 1) if i > 0 else '') + rcnn_pred_list.append((cls_score, bbox_pred)) + + # get mask rois + rois = proposal_list[2] + + if mode == 'train': + loss = self.bbox_head.get_loss(rcnn_pred_list, rcnn_target_list, + self.cascade_rcnn_loss_weight) + loss.update(rpn_loss) + + labels_int32 = rcnn_target_list[2][1] + + mask_rois, roi_has_mask_int32, mask_int32 = self.mask_assigner( + rois=rois, + gt_classes=feed_vars['gt_label'], + is_crowd=feed_vars['is_crowd'], + gt_segms=feed_vars['gt_mask'], + im_info=feed_vars['im_info'], + labels_int32=labels_int32) + + if self.fpn is None: + bbox_head_feat = self.bbox_head.get_head_feat() + feat = fluid.layers.gather(bbox_head_feat, roi_has_mask_int32) + else: + feat = self.roi_extractor( + body_feats, mask_rois, spatial_scale, is_mask=True) + mask_loss = self.mask_head.get_loss(feat, mask_int32) + loss.update(mask_loss) + + total_loss = fluid.layers.sum(list(loss.values())) + loss.update({'loss': total_loss}) + return loss + else: + mask_name = 'mask_pred' + mask_pred, bbox_pred = self.single_scale_eval( + body_feats, spatial_scale, im_info, mask_name, bbox_pred, + roi_feat_list, rcnn_pred_list, proposal_list, + feed_vars['im_shape']) + return {'bbox': bbox_pred, 'mask': mask_pred} + + def build_multi_scale(self, feed_vars, mask_branch=False): + 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]) + body_feats = self.backbone(im) + result.update(body_feats) + + # FPN + if self.fpn is not None: + body_feats, spatial_scale = self.fpn.get_output(body_feats) + rois = self.rpn_head.get_proposals(body_feats, im_info, mode='test') + if not mask_branch: + im_shape = feed_vars['im_shape'] + body_feat_names = list(body_feats.keys()) + proposal_list = [] + roi_feat_list = [] + rcnn_pred_list = [] + + proposals = None + bbox_pred = None + for i in range(3): + if i > 0: + refined_bbox = self._decode_box( + proposals, + bbox_pred, + curr_stage=i - 1, ) + else: + refined_bbox = rois + + proposals = refined_bbox + proposal_list.append(proposals) + + # extract roi features + roi_feat = self.roi_extractor(body_feats, proposals, + spatial_scale) + roi_feat_list.append(roi_feat) + + # bbox head + cls_score, bbox_pred = self.bbox_head.get_output( + roi_feat, + wb_scalar=1.0 / self.cascade_rcnn_loss_weight[i], + name='_' + str(i + 1) if i > 0 else '') + rcnn_pred_list.append((cls_score, bbox_pred)) + + # get mask rois + if self.fpn is None: + body_feat = body_feats[body_feat_names[-1]] + pred = self.bbox_head.get_prediction( + im_info, + im_shape, + roi_feat_list, + rcnn_pred_list, + proposal_list, + self.cascade_bbox_reg_weights, + return_box_score=True) + bbox_name = 'bbox_' + str(i) + score_name = 'score_' + str(i) + if 'flip' in im.name: + bbox_name += '_flip' + score_name += '_flip' + result[bbox_name] = pred['bbox'] + result[score_name] = pred['score'] + 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'] + mask_pred, bbox_pred = self.single_scale_eval( + body_feats, + spatial_scale, + im_info, + mask_name, + bbox_pred=bbox_pred, + use_multi_test=True) + result[mask_name] = mask_pred + return result + + def single_scale_eval(self, + body_feats, + spatial_scale, + im_info, + mask_name, + bbox_pred, + roi_feat_list=None, + rcnn_pred_list=None, + proposal_list=None, + im_shape=None, + use_multi_test=False): + if self.fpn is None: + last_feat = body_feats[list(body_feats.keys())[-1]] + if not use_multi_test: + bbox_pred = self.bbox_head.get_prediction( + im_info, im_shape, roi_feat_list, rcnn_pred_list, proposal_list, + self.cascade_bbox_reg_weights) + bbox_pred = bbox_pred['bbox'] + + # share weight + bbox_shape = fluid.layers.shape(bbox_pred) + bbox_size = fluid.layers.reduce_prod(bbox_shape) + bbox_size = fluid.layers.reshape(bbox_size, [1, 1]) + size = fluid.layers.fill_constant([1, 1], value=6, dtype='int32') + cond = fluid.layers.less_than(x=bbox_size, y=size) + + mask_pred = fluid.layers.create_global_var( + shape=[1], + value=0.0, + dtype='float32', + persistable=False, + name=mask_name) + with fluid.layers.control_flow.Switch() as switch: + with switch.case(cond): + fluid.layers.assign(input=bbox_pred, output=mask_pred) + with switch.default(): + bbox = fluid.layers.slice(bbox_pred, [1], starts=[2], ends=[6]) + + im_scale = fluid.layers.slice( + im_info, [1], starts=[2], ends=[3]) + im_scale = fluid.layers.sequence_expand(im_scale, bbox) + + mask_rois = bbox * im_scale + if self.fpn is None: + mask_feat = self.roi_extractor(last_feat, mask_rois) + mask_feat = self.bbox_head.get_head_feat(mask_feat) + else: + mask_feat = self.roi_extractor( + body_feats, mask_rois, spatial_scale, is_mask=True) + + mask_out = self.mask_head.get_prediction(mask_feat, bbox) + fluid.layers.assign(input=mask_out, output=mask_pred) + return mask_pred, bbox_pred + + def _input_check(self, require_fields, feed_vars): + for var in require_fields: + assert var in feed_vars, \ + "{} has no {} field".format(feed_vars, var) + + def _decode_box(self, proposals, bbox_pred, curr_stage): + rcnn_loc_delta_r = fluid.layers.reshape( + bbox_pred, (-1, self.cls_agnostic_bbox_reg, 4)) + # only use fg box delta to decode box + rcnn_loc_delta_s = fluid.layers.slice( + rcnn_loc_delta_r, axes=[1], starts=[1], ends=[2]) + refined_bbox = fluid.layers.box_coder( + prior_box=proposals, + prior_box_var=self.cascade_bbox_reg_weights[curr_stage], + target_box=rcnn_loc_delta_s, + code_type='decode_center_size', + box_normalized=False, + axis=1, ) + refined_bbox = fluid.layers.reshape(refined_bbox, shape=[-1, 4]) + + return refined_bbox + + def train(self, feed_vars): + return self.build(feed_vars, 'train') + + def eval(self, feed_vars, multi_scale=None, mask_branch=False): + if multi_scale: + return self.build_multi_scale(feed_vars, mask_branch) + return self.build(feed_vars, 'test') + + def test(self, feed_vars): + return self.build(feed_vars, 'test') diff --git a/ppdet/modeling/architectures/cascade_rcnn.py b/ppdet/modeling/architectures/cascade_rcnn.py new file mode 100644 index 0000000000000000000000000000000000000000..b80a8d7f62ccd13f632fe8124372156d656d2abc --- /dev/null +++ b/ppdet/modeling/architectures/cascade_rcnn.py @@ -0,0 +1,289 @@ +# 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 + +from collections import OrderedDict + +import paddle.fluid as fluid + +from ppdet.experimental import mixed_precision_global_state +from ppdet.core.workspace import register + +__all__ = ['CascadeRCNN'] + + +@register +class CascadeRCNN(object): + """ + Cascade R-CNN architecture, see https://arxiv.org/abs/1712.00726 + + Args: + backbone (object): backbone instance + rpn_head (object): `RPNhead` instance + bbox_assigner (object): `BBoxAssigner` instance + roi_extractor (object): ROI extractor instance + bbox_head (object): `BBoxHead` instance + fpn (object): feature pyramid network instance + """ + + __category__ = 'architecture' + __inject__ = [ + 'backbone', 'fpn', 'rpn_head', 'bbox_assigner', 'roi_extractor', + 'bbox_head' + ] + + def __init__(self, + backbone, + rpn_head, + roi_extractor='FPNRoIAlign', + bbox_head='CascadeBBoxHead', + bbox_assigner='CascadeBBoxAssigner', + rpn_only=False, + fpn='FPN'): + super(CascadeRCNN, self).__init__() + assert fpn is not None, "cascade RCNN requires FPN" + self.backbone = backbone + self.fpn = fpn + self.rpn_head = rpn_head + self.bbox_assigner = bbox_assigner + self.roi_extractor = roi_extractor + self.bbox_head = bbox_head + self.rpn_only = rpn_only + # Cascade local cfg + self.cls_agnostic_bbox_reg = 2 + (brw0, brw1, brw2) = self.bbox_assigner.bbox_reg_weights + self.cascade_bbox_reg_weights = [ + [1. / brw0, 1. / brw0, 2. / brw0, 2. / brw0], + [1. / brw1, 1. / brw1, 2. / brw1, 2. / brw1], + [1. / brw2, 1. / brw2, 2. / brw2, 2. / brw2] + ] + self.cascade_rcnn_loss_weight = [1.0, 0.5, 0.25] + + def build(self, feed_vars, mode='train'): + if mode == 'train': + required_fields = ['gt_label', 'gt_box', 'is_crowd', 'im_info'] + else: + required_fields = ['im_shape', 'im_info'] + self._input_check(required_fields, feed_vars) + + im = feed_vars['image'] + im_info = feed_vars['im_info'] + + if mode == 'train': + gt_box = feed_vars['gt_box'] + is_crowd = feed_vars['is_crowd'] + + mixed_precision_enabled = mixed_precision_global_state() is not None + # cast inputs to FP16 + if mixed_precision_enabled: + im = fluid.layers.cast(im, 'float16') + + # backbone + body_feats = self.backbone(im) + + # cast features back to FP32 + if mixed_precision_enabled: + body_feats = OrderedDict((k, fluid.layers.cast(v, 'float32')) + for k, v in body_feats.items()) + + # FPN + if self.fpn is not None: + body_feats, spatial_scale = self.fpn.get_output(body_feats) + + # rpn proposals + 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) + else: + if self.rpn_only: + im_scale = fluid.layers.slice( + im_info, [1], starts=[2], ends=[3]) + im_scale = fluid.layers.sequence_expand(im_scale, rpn_rois) + rois = rpn_rois / im_scale + return {'proposal': rois} + + proposal_list = [] + roi_feat_list = [] + rcnn_pred_list = [] + rcnn_target_list = [] + + proposals = None + bbox_pred = None + for i in range(3): + if i > 0: + refined_bbox = self._decode_box( + proposals, + bbox_pred, + curr_stage=i - 1, ) + else: + refined_bbox = rpn_rois + + if mode == 'train': + outs = self.bbox_assigner( + input_rois=refined_bbox, feed_vars=feed_vars, curr_stage=i) + + proposals = outs[0] + rcnn_target_list.append(outs) + else: + proposals = refined_bbox + proposal_list.append(proposals) + + # extract roi features + roi_feat = self.roi_extractor(body_feats, proposals, spatial_scale) + roi_feat_list.append(roi_feat) + + # bbox head + cls_score, bbox_pred = self.bbox_head.get_output( + roi_feat, + wb_scalar=1.0 / self.cascade_rcnn_loss_weight[i], + name='_' + str(i + 1) if i > 0 else '') + rcnn_pred_list.append((cls_score, bbox_pred)) + + if mode == 'train': + 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())) + loss.update({'loss': total_loss}) + return loss + else: + pred = self.bbox_head.get_prediction( + im_info, feed_vars['im_shape'], roi_feat_list, rcnn_pred_list, + proposal_list, self.cascade_bbox_reg_weights, + self.cls_agnostic_bbox_reg) + return pred + + 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'] + + # backbone + body_feats = self.backbone(im) + result.update(body_feats) + body_feat_names = list(body_feats.keys()) + + # FPN + if self.fpn is not None: + body_feats, spatial_scale = self.fpn.get_output(body_feats) + + # rpn proposals + rpn_rois = self.rpn_head.get_proposals( + body_feats, im_info, mode='test') + + proposal_list = [] + roi_feat_list = [] + rcnn_pred_list = [] + + proposals = None + bbox_pred = None + for i in range(3): + if i > 0: + refined_bbox = self._decode_box( + proposals, + bbox_pred, + curr_stage=i - 1, ) + else: + refined_bbox = rpn_rois + + proposals = refined_bbox + proposal_list.append(proposals) + + # extract roi features + roi_feat = self.roi_extractor(body_feats, proposals, + spatial_scale) + roi_feat_list.append(roi_feat) + + # bbox head + cls_score, bbox_pred = self.bbox_head.get_output( + roi_feat, + wb_scalar=1.0 / self.cascade_rcnn_loss_weight[i], + name='_' + str(i + 1) if i > 0 else '') + rcnn_pred_list.append((cls_score, bbox_pred)) + + # get mask rois + rois = proposal_list[2] + + 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) + + pred = self.bbox_head.get_prediction( + im_info, + im_shape, + roi_feat_list, + rcnn_pred_list, + proposal_list, + self.cascade_bbox_reg_weights, + self.cls_agnostic_bbox_reg, + return_box_score=True) + bbox_name = 'bbox_' + str(i) + score_name = 'score_' + str(i) + if 'flip' in im.name: + bbox_name += '_flip' + score_name += '_flip' + result[bbox_name] = pred['bbox'] + result[score_name] = pred['score'] + return result + + def _input_check(self, require_fields, feed_vars): + for var in require_fields: + assert var in feed_vars, \ + "{} has no {} field".format(feed_vars, var) + + def _decode_box(self, proposals, bbox_pred, curr_stage): + rcnn_loc_delta_r = fluid.layers.reshape( + bbox_pred, (-1, self.cls_agnostic_bbox_reg, 4)) + # only use fg box delta to decode box + rcnn_loc_delta_s = fluid.layers.slice( + rcnn_loc_delta_r, axes=[1], starts=[1], ends=[2]) + refined_bbox = fluid.layers.box_coder( + prior_box=proposals, + prior_box_var=self.cascade_bbox_reg_weights[curr_stage], + target_box=rcnn_loc_delta_s, + code_type='decode_center_size', + box_normalized=False, + axis=1, ) + refined_bbox = fluid.layers.reshape(refined_bbox, shape=[-1, 4]) + + return refined_bbox + + def train(self, feed_vars): + return self.build(feed_vars, 'train') + + def eval(self, feed_vars, multi_scale=None): + if multi_scale: + return self.build_multi_scale(feed_vars) + return self.build(feed_vars, 'test') + + def test(self, feed_vars): + return self.build(feed_vars, 'test') diff --git a/ppdet/modeling/architectures/faceboxes.py b/ppdet/modeling/architectures/faceboxes.py new file mode 100644 index 0000000000000000000000000000000000000000..194b3a7e864f30051a20e514bbf9a1e970548afc --- /dev/null +++ b/ppdet/modeling/architectures/faceboxes.py @@ -0,0 +1,154 @@ +# 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 +from paddle import fluid + +from paddle.fluid.param_attr import ParamAttr +from paddle.fluid.regularizer import L2Decay + +from ppdet.core.workspace import register +from ppdet.modeling.ops import SSDOutputDecoder + +__all__ = ['FaceBoxes'] + + +@register +class FaceBoxes(object): + """ + FaceBoxes: Sub-millisecond Neural Face Detection on Mobile GPUs, + see https://https://arxiv.org/abs/1708.05234 + + Args: + backbone (object): backbone instance + output_decoder (object): `SSDOutputDecoder` instance + densities (list|None): the densities of generated density prior boxes, + this attribute should be a list or tuple of integers. + fixed_sizes (list|None): the fixed sizes of generated density prior boxes, + this attribute should a list or tuple of same length with `densities`. + num_classes (int): number of output classes + """ + + __category__ = 'architecture' + __inject__ = ['backbone', 'output_decoder'] + __shared__ = ['num_classes'] + + def __init__(self, + backbone="FaceBoxNet", + output_decoder=SSDOutputDecoder().__dict__, + densities=[[4, 2, 1], [1], [1]], + fixed_sizes=[[32., 64., 128.], [256.], [512.]], + num_classes=2): + super(FaceBoxes, self).__init__() + self.backbone = backbone + self.num_classes = num_classes + self.output_decoder = output_decoder + if isinstance(output_decoder, dict): + self.output_decoder = SSDOutputDecoder(**output_decoder) + self.densities = densities + self.fixed_sizes = fixed_sizes + + 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'] + + body_feats = self.backbone(im) + locs, confs, box, box_var = self._multi_box_head( + 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, + box_var, + overlap_threshold=0.35, + neg_overlap=0.35) + loss = fluid.layers.reduce_sum(loss) + loss.persistable = True + return {'loss': loss} + else: + pred = self.output_decoder(locs, confs, box, box_var) + return {'bbox': pred} + + def _multi_box_head(self, inputs, image, num_classes=2): + def permute_and_reshape(input, last_dim): + trans = fluid.layers.transpose(input, perm=[0, 2, 3, 1]) + compile_shape = [0, -1, last_dim] + return fluid.layers.reshape(trans, shape=compile_shape) + + def _is_list_or_tuple_(data): + return (isinstance(data, list) or isinstance(data, tuple)) + + locs, confs = [], [] + boxes, vars = [], [] + b_attr = ParamAttr(learning_rate=2., regularizer=L2Decay(0.)) + + for i, input in enumerate(inputs): + densities = self.densities[i] + fixed_sizes = self.fixed_sizes[i] + box, var = fluid.layers.density_prior_box( + input, + image, + densities=densities, + fixed_sizes=fixed_sizes, + fixed_ratios=[1.], + clip=False, + offset=0.5) + + num_boxes = box.shape[2] + + box = fluid.layers.reshape(box, shape=[-1, 4]) + var = fluid.layers.reshape(var, shape=[-1, 4]) + num_loc_output = num_boxes * 4 + num_conf_output = num_boxes * num_classes + # get loc + mbox_loc = fluid.layers.conv2d( + input, num_loc_output, 3, 1, 1, bias_attr=b_attr) + loc = permute_and_reshape(mbox_loc, 4) + # get conf + mbox_conf = fluid.layers.conv2d( + input, num_conf_output, 3, 1, 1, bias_attr=b_attr) + conf = permute_and_reshape(mbox_conf, 2) + + locs.append(loc) + confs.append(conf) + boxes.append(box) + vars.append(var) + + face_mbox_loc = fluid.layers.concat(locs, axis=1) + face_mbox_conf = fluid.layers.concat(confs, axis=1) + prior_boxes = fluid.layers.concat(boxes) + box_vars = fluid.layers.concat(vars) + return face_mbox_loc, face_mbox_conf, prior_boxes, box_vars + + def train(self, feed_vars): + return self.build(feed_vars, 'train') + + def eval(self, feed_vars): + return self.build(feed_vars, 'eval') + + def test(self, feed_vars): + return self.build(feed_vars, 'test') + + def is_bbox_normalized(self): + return True diff --git a/ppdet/modeling/architectures/faster_rcnn.py b/ppdet/modeling/architectures/faster_rcnn.py new file mode 100644 index 0000000000000000000000000000000000000000..e0ef7355c0d358d7b409ce3080f2416cd38de0b3 --- /dev/null +++ b/ppdet/modeling/architectures/faster_rcnn.py @@ -0,0 +1,204 @@ +# 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 + +from collections import OrderedDict + +from paddle import fluid + +from ppdet.experimental import mixed_precision_global_state +from ppdet.core.workspace import register + +__all__ = ['FasterRCNN'] + + +@register +class FasterRCNN(object): + """ + Faster R-CNN architecture, see https://arxiv.org/abs/1506.01497 + Args: + backbone (object): backbone instance + rpn_head (object): `RPNhead` instance + bbox_assigner (object): `BBoxAssigner` instance + roi_extractor (object): ROI extractor instance + bbox_head (object): `BBoxHead` instance + fpn (object): feature pyramid network instance + """ + + __category__ = 'architecture' + __inject__ = [ + 'backbone', 'rpn_head', 'bbox_assigner', 'roi_extractor', 'bbox_head', + 'fpn' + ] + + def __init__(self, + backbone, + rpn_head, + roi_extractor, + bbox_head='BBoxHead', + bbox_assigner='BBoxAssigner', + rpn_only=False, + fpn=None): + super(FasterRCNN, self).__init__() + self.backbone = backbone + self.rpn_head = rpn_head + self.bbox_assigner = bbox_assigner + self.roi_extractor = roi_extractor + self.bbox_head = bbox_head + self.fpn = fpn + self.rpn_only = rpn_only + + def build(self, feed_vars, mode='train'): + if mode == 'train': + required_fields = ['gt_label', 'gt_box', 'is_crowd', 'im_info'] + else: + required_fields = ['im_shape', 'im_info'] + self._input_check(required_fields, feed_vars) + + im = feed_vars['image'] + im_info = feed_vars['im_info'] + if mode == 'train': + gt_box = feed_vars['gt_box'] + is_crowd = feed_vars['is_crowd'] + else: + im_shape = feed_vars['im_shape'] + + mixed_precision_enabled = mixed_precision_global_state() is not None + + # cast inputs to FP16 + if mixed_precision_enabled: + im = fluid.layers.cast(im, 'float16') + + body_feats = self.backbone(im) + body_feat_names = list(body_feats.keys()) + + # cast features back to FP32 + if mixed_precision_enabled: + body_feats = OrderedDict((k, fluid.layers.cast(v, 'float32')) + for k, v in body_feats.items()) + + if self.fpn is not None: + body_feats, spatial_scale = self.fpn.get_output(body_feats) + + 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) + # sampled rpn proposals + for var in ['gt_label', 'is_crowd', 'gt_box', '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'], + is_crowd=feed_vars['is_crowd'], + gt_boxes=feed_vars['gt_box'], + im_info=feed_vars['im_info']) + + rois = outs[0] + labels_int32 = outs[1] + bbox_targets = outs[2] + bbox_inside_weights = outs[3] + bbox_outside_weights = outs[4] + else: + if self.rpn_only: + im_scale = fluid.layers.slice( + im_info, [1], starts=[2], ends=[3]) + im_scale = fluid.layers.sequence_expand(im_scale, rois) + rois = rois / im_scale + return {'proposal': rois} + if self.fpn is None: + # in models without FPN, roi extractor only uses the last level of + # feature maps. And body_feat_names[-1] represents the name of + # last feature map. + body_feat = body_feats[body_feat_names[-1]] + roi_feat = self.roi_extractor(body_feat, rois) + else: + roi_feat = self.roi_extractor(body_feats, rois, spatial_scale) + + if mode == 'train': + loss = self.bbox_head.get_loss(roi_feat, labels_int32, bbox_targets, + bbox_inside_weights, + bbox_outside_weights) + loss.update(rpn_loss) + total_loss = fluid.layers.sum(list(loss.values())) + loss.update({'loss': total_loss}) + return loss + else: + pred = self.bbox_head.get_prediction(roi_feat, rois, im_info, + im_shape) + return pred + + 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'] + body_feats = self.backbone(im) + result.update(body_feats) + body_feat_names = list(body_feats.keys()) + + if self.fpn is not None: + body_feats, spatial_scale = self.fpn.get_output(body_feats) + + rois = self.rpn_head.get_proposals(body_feats, im_info, mode='test') + + if self.fpn is None: + # in models without FPN, roi extractor only uses the last level of + # feature maps. And body_feat_names[-1] represents the name of + # last feature map. + body_feat = body_feats[body_feat_names[-1]] + roi_feat = self.roi_extractor(body_feat, rois) + else: + roi_feat = self.roi_extractor(body_feats, rois, spatial_scale) + + pred = self.bbox_head.get_prediction( + roi_feat, rois, im_info, im_shape, return_box_score=True) + bbox_name = 'bbox_' + str(i) + score_name = 'score_' + str(i) + if 'flip' in im.name: + bbox_name += '_flip' + score_name += '_flip' + result[bbox_name] = pred['bbox'] + result[score_name] = pred['score'] + return result + + def _input_check(self, require_fields, feed_vars): + for var in require_fields: + assert var in feed_vars, \ + "{} has no {} field".format(feed_vars, var) + + def train(self, feed_vars): + return self.build(feed_vars, 'train') + + def eval(self, feed_vars, multi_scale=None): + if multi_scale: + return self.build_multi_scale(feed_vars) + return self.build(feed_vars, 'test') + + def test(self, feed_vars): + return self.build(feed_vars, 'test') diff --git a/ppdet/modeling/architectures/mask_rcnn.py b/ppdet/modeling/architectures/mask_rcnn.py new file mode 100644 index 0000000000000000000000000000000000000000..715f3efa90e52c3330f36e9ba787082722f2c8bb --- /dev/null +++ b/ppdet/modeling/architectures/mask_rcnn.py @@ -0,0 +1,285 @@ +# 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 + +from collections import OrderedDict + +import paddle.fluid as fluid + +from ppdet.experimental import mixed_precision_global_state +from ppdet.core.workspace import register + +__all__ = ['MaskRCNN'] + + +@register +class MaskRCNN(object): + """ + Mask R-CNN architecture, see https://arxiv.org/abs/1703.06870 + Args: + backbone (object): backbone instance + rpn_head (object): `RPNhead` instance + bbox_assigner (object): `BBoxAssigner` instance + roi_extractor (object): ROI extractor instance + bbox_head (object): `BBoxHead` instance + mask_assigner (object): `MaskAssigner` instance + mask_head (object): `MaskHead` instance + fpn (object): feature pyramid network instance + """ + + __category__ = 'architecture' + __inject__ = [ + 'backbone', 'rpn_head', 'bbox_assigner', 'roi_extractor', 'bbox_head', + 'mask_assigner', 'mask_head', 'fpn' + ] + + def __init__(self, + backbone, + rpn_head, + bbox_head='BBoxHead', + bbox_assigner='BBoxAssigner', + roi_extractor='RoIAlign', + mask_assigner='MaskAssigner', + mask_head='MaskHead', + rpn_only=False, + fpn=None): + super(MaskRCNN, self).__init__() + self.backbone = backbone + self.rpn_head = rpn_head + self.bbox_assigner = bbox_assigner + self.roi_extractor = roi_extractor + self.bbox_head = bbox_head + self.mask_assigner = mask_assigner + self.mask_head = mask_head + self.rpn_only = rpn_only + self.fpn = fpn + + def build(self, feed_vars, mode='train'): + if mode == 'train': + required_fields = [ + 'gt_label', 'gt_box', 'gt_mask', 'is_crowd', 'im_info' + ] + else: + required_fields = ['im_shape', 'im_info'] + self._input_check(required_fields, feed_vars) + im = feed_vars['image'] + im_info = feed_vars['im_info'] + + mixed_precision_enabled = mixed_precision_global_state() is not None + # cast inputs to FP16 + if mixed_precision_enabled: + im = fluid.layers.cast(im, 'float16') + + # backbone + body_feats = self.backbone(im) + + # cast features back to FP32 + if mixed_precision_enabled: + body_feats = OrderedDict((k, fluid.layers.cast(v, 'float32')) + for k, v in body_feats.items()) + + # FPN + spatial_scale = None + if self.fpn is not None: + body_feats, spatial_scale = self.fpn.get_output(body_feats) + + # RPN proposals + 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'], + feed_vars['is_crowd']) + + outs = self.bbox_assigner( + rpn_rois=rois, + gt_classes=feed_vars['gt_label'], + is_crowd=feed_vars['is_crowd'], + gt_boxes=feed_vars['gt_box'], + im_info=feed_vars['im_info']) + rois = outs[0] + labels_int32 = outs[1] + + 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) + + loss = self.bbox_head.get_loss(roi_feat, labels_int32, *outs[2:]) + loss.update(rpn_loss) + + mask_rois, roi_has_mask_int32, mask_int32 = self.mask_assigner( + rois=rois, + gt_classes=feed_vars['gt_label'], + is_crowd=feed_vars['is_crowd'], + gt_segms=feed_vars['gt_mask'], + im_info=feed_vars['im_info'], + labels_int32=labels_int32) + if self.fpn is None: + bbox_head_feat = self.bbox_head.get_head_feat() + feat = fluid.layers.gather(bbox_head_feat, roi_has_mask_int32) + else: + feat = self.roi_extractor( + body_feats, mask_rois, spatial_scale, is_mask=True) + + mask_loss = self.mask_head.get_loss(feat, mask_int32) + loss.update(mask_loss) + + total_loss = fluid.layers.sum(list(loss.values())) + loss.update({'loss': total_loss}) + return loss + + else: + if self.rpn_only: + im_scale = fluid.layers.slice( + im_info, [1], starts=[2], ends=[3]) + im_scale = fluid.layers.sequence_expand(im_scale, rois) + rois = rois / im_scale + return {'proposal': rois} + mask_name = 'mask_pred' + mask_pred, bbox_pred = self.single_scale_eval( + body_feats, mask_name, rois, im_info, feed_vars['im_shape'], + spatial_scale) + return {'bbox': bbox_pred, 'mask': mask_pred} + + def build_multi_scale(self, feed_vars, mask_branch=False): + 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]) + body_feats = self.backbone(im) + result.update(body_feats) + + # FPN + if self.fpn is not None: + body_feats, spatial_scale = self.fpn.get_output(body_feats) + rois = self.rpn_head.get_proposals(body_feats, im_info, mode='test') + if not mask_branch: + im_shape = feed_vars['im_shape'] + body_feat_names = list(body_feats.keys()) + if self.fpn is None: + body_feat = body_feats[body_feat_names[-1]] + roi_feat = self.roi_extractor(body_feat, rois) + else: + roi_feat = self.roi_extractor(body_feats, rois, + spatial_scale) + pred = self.bbox_head.get_prediction( + roi_feat, rois, im_info, im_shape, return_box_score=True) + bbox_name = 'bbox_' + str(i) + score_name = 'score_' + str(i) + if 'flip' in im.name: + bbox_name += '_flip' + score_name += '_flip' + result[bbox_name] = pred['bbox'] + result[score_name] = pred['score'] + 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'] + mask_pred, bbox_pred = self.single_scale_eval( + body_feats, mask_name, rois, im_info, feed_vars['im_shape'], + spatial_scale, bbox_pred) + result[mask_name] = mask_pred + return result + + def single_scale_eval(self, + body_feats, + mask_name, + rois, + im_info, + 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: + bbox_pred = self.bbox_head.get_prediction(roi_feat, rois, im_info, + im_shape) + bbox_pred = bbox_pred['bbox'] + + # share weight + bbox_shape = fluid.layers.shape(bbox_pred) + bbox_size = fluid.layers.reduce_prod(bbox_shape) + bbox_size = fluid.layers.reshape(bbox_size, [1, 1]) + size = fluid.layers.fill_constant([1, 1], value=6, dtype='int32') + cond = fluid.layers.less_than(x=bbox_size, y=size) + + mask_pred = fluid.layers.create_global_var( + shape=[1], + value=0.0, + dtype='float32', + persistable=False, + name=mask_name) + with fluid.layers.control_flow.Switch() as switch: + with switch.case(cond): + fluid.layers.assign(input=bbox_pred, output=mask_pred) + with switch.default(): + bbox = fluid.layers.slice(bbox_pred, [1], starts=[2], ends=[6]) + + im_scale = fluid.layers.slice( + im_info, [1], starts=[2], ends=[3]) + im_scale = fluid.layers.sequence_expand(im_scale, bbox) + + mask_rois = bbox * im_scale + if self.fpn is None: + mask_feat = self.roi_extractor(last_feat, mask_rois) + mask_feat = self.bbox_head.get_head_feat(mask_feat) + else: + mask_feat = self.roi_extractor( + body_feats, mask_rois, spatial_scale, is_mask=True) + + mask_out = self.mask_head.get_prediction(mask_feat, bbox) + fluid.layers.assign(input=mask_out, output=mask_pred) + return mask_pred, bbox_pred + + def _input_check(self, require_fields, feed_vars): + for var in require_fields: + assert var in feed_vars, \ + "{} has no {} field".format(feed_vars, var) + + def train(self, feed_vars): + return self.build(feed_vars, 'train') + + def eval(self, feed_vars, multi_scale=None, mask_branch=False): + if multi_scale: + return self.build_multi_scale(feed_vars, mask_branch) + return self.build(feed_vars, 'test') + + def test(self, feed_vars): + return self.build(feed_vars, 'test') diff --git a/ppdet/modeling/architectures/retinanet.py b/ppdet/modeling/architectures/retinanet.py new file mode 100644 index 0000000000000000000000000000000000000000..4ce5ac500c65fa713b3735b480e8e40b9b123063 --- /dev/null +++ b/ppdet/modeling/architectures/retinanet.py @@ -0,0 +1,92 @@ +# 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 + +from collections import OrderedDict + +import paddle.fluid as fluid + +from ppdet.experimental import mixed_precision_global_state +from ppdet.core.workspace import register + +__all__ = ['RetinaNet'] + + +@register +class RetinaNet(object): + """ + RetinaNet architecture, see https://arxiv.org/abs/1708.02002 + + Args: + backbone (object): backbone instance + fpn (object): feature pyramid network instance + retina_head (object): `RetinaHead` instance + """ + + __category__ = 'architecture' + __inject__ = ['backbone', 'fpn', 'retina_head'] + + def __init__(self, backbone, fpn, retina_head): + super(RetinaNet, self).__init__() + self.backbone = backbone + self.fpn = fpn + self.retina_head = retina_head + + def build(self, feed_vars, mode='train'): + 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'] + is_crowd = feed_vars['is_crowd'] + + mixed_precision_enabled = mixed_precision_global_state() is not None + # cast inputs to FP16 + if mixed_precision_enabled: + im = fluid.layers.cast(im, 'float16') + + # backbone + body_feats = self.backbone(im) + + # cast features back to FP32 + if mixed_precision_enabled: + body_feats = OrderedDict((k, fluid.layers.cast(v, 'float32')) + for k, v in body_feats.items()) + + # FPN + body_feats, spatial_scale = self.fpn.get_output(body_feats) + + # retinanet head + if mode == 'train': + loss = self.retina_head.get_loss(body_feats, spatial_scale, im_info, + gt_box, gt_label, is_crowd) + total_loss = fluid.layers.sum(list(loss.values())) + loss.update({'loss': total_loss}) + return loss + else: + pred = self.retina_head.get_prediction(body_feats, spatial_scale, + im_info) + return pred + + def train(self, feed_vars): + return self.build(feed_vars, 'train') + + def eval(self, feed_vars): + return self.build(feed_vars, 'test') + + def test(self, feed_vars): + return self.build(feed_vars, 'test') diff --git a/ppdet/modeling/architectures/ssd.py b/ppdet/modeling/architectures/ssd.py new file mode 100644 index 0000000000000000000000000000000000000000..e899075f21291a38a5af0e4c8d5e70af4e55eaec --- /dev/null +++ b/ppdet/modeling/architectures/ssd.py @@ -0,0 +1,105 @@ +# 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 + +from collections import OrderedDict + +import paddle.fluid as fluid + +from ppdet.experimental import mixed_precision_global_state +from ppdet.core.workspace import register +from ppdet.modeling.ops import SSDOutputDecoder + +__all__ = ['SSD'] + + +@register +class SSD(object): + """ + Single Shot MultiBox Detector, see https://arxiv.org/abs/1512.02325 + + Args: + backbone (object): backbone instance + multi_box_head (object): `MultiBoxHead` instance + output_decoder (object): `SSDOutputDecoder` instance + num_classes (int): number of output classes + """ + + __category__ = 'architecture' + __inject__ = ['backbone', 'multi_box_head', 'output_decoder'] + __shared__ = ['num_classes'] + + def __init__(self, + backbone, + multi_box_head='MultiBoxHead', + output_decoder=SSDOutputDecoder().__dict__, + num_classes=21): + super(SSD, self).__init__() + self.backbone = backbone + self.multi_box_head = multi_box_head + self.num_classes = num_classes + self.output_decoder = output_decoder + if isinstance(output_decoder, dict): + self.output_decoder = SSDOutputDecoder(**output_decoder) + + 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'] + + mixed_precision_enabled = mixed_precision_global_state() is not None + # cast inputs to FP16 + if mixed_precision_enabled: + im = fluid.layers.cast(im, 'float16') + + # backbone + body_feats = self.backbone(im) + + if isinstance(body_feats, OrderedDict): + body_feat_names = list(body_feats.keys()) + body_feats = [body_feats[name] for name in body_feat_names] + + # cast features back to FP32 + if mixed_precision_enabled: + body_feats = [fluid.layers.cast(v, 'float32') for v in body_feats] + + locs, confs, box, box_var = self.multi_box_head( + 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, + box_var) + loss = fluid.layers.reduce_sum(loss) + return {'loss': loss} + else: + pred = self.output_decoder(locs, confs, box, box_var) + return {'bbox': pred} + + def train(self, feed_vars): + return self.build(feed_vars, 'train') + + def eval(self, feed_vars): + return self.build(feed_vars, 'eval') + + def test(self, feed_vars): + return self.build(feed_vars, 'test') + + def is_bbox_normalized(self): + # SSD use output_decoder in output layers, bbox is normalized + # to range [0, 1], is_bbox_normalized is used in eval.py and infer.py + return True diff --git a/ppdet/modeling/architectures/yolov3.py b/ppdet/modeling/architectures/yolov3.py new file mode 100644 index 0000000000000000000000000000000000000000..2912ffda5215af594d57255397b8a572455aa090 --- /dev/null +++ b/ppdet/modeling/architectures/yolov3.py @@ -0,0 +1,86 @@ +# 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 + +from collections import OrderedDict + +from paddle import fluid + +from ppdet.experimental import mixed_precision_global_state +from ppdet.core.workspace import register + +__all__ = ['YOLOv3'] + + +@register +class YOLOv3(object): + """ + YOLOv3 network, see https://arxiv.org/abs/1804.02767 + + Args: + backbone (object): an backbone instance + yolo_head (object): an `YOLOv3Head` instance + """ + + __category__ = 'architecture' + __inject__ = ['backbone', 'yolo_head'] + + def __init__(self, backbone, yolo_head='YOLOv3Head'): + super(YOLOv3, self).__init__() + self.backbone = backbone + self.yolo_head = yolo_head + + def build(self, feed_vars, mode='train'): + im = feed_vars['image'] + + mixed_precision_enabled = mixed_precision_global_state() is not None + + # cast inputs to FP16 + if mixed_precision_enabled: + im = fluid.layers.cast(im, 'float16') + + body_feats = self.backbone(im) + + if isinstance(body_feats, OrderedDict): + body_feat_names = list(body_feats.keys()) + body_feats = [body_feats[name] for name in body_feat_names] + + # cast features back to FP32 + if mixed_precision_enabled: + 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_score = feed_vars['gt_score'] + + return { + 'loss': self.yolo_head.get_loss(body_feats, gt_box, gt_label, + gt_score) + } + else: + im_size = feed_vars['im_size'] + return self.yolo_head.get_prediction(body_feats, im_size) + + def train(self, feed_vars): + return self.build(feed_vars, mode='train') + + def eval(self, feed_vars): + return self.build(feed_vars, mode='test') + + def test(self, feed_vars): + return self.build(feed_vars, mode='test') diff --git a/ppdet/modeling/backbones/__init__.py b/ppdet/modeling/backbones/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..48081429fecd83ad086fb66fb7eac99e0cc96fb5 --- /dev/null +++ b/ppdet/modeling/backbones/__init__.py @@ -0,0 +1,35 @@ +# 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 . import resnet +from . import resnext +from . import darknet +from . import mobilenet +from . import senet +from . import fpn +from . import vgg +from . import blazenet +from . import faceboxnet + +from .resnet import * +from .resnext import * +from .darknet import * +from .mobilenet import * +from .senet import * +from .fpn import * +from .vgg import * +from .blazenet import * +from .faceboxnet import * diff --git a/ppdet/modeling/backbones/blazenet.py b/ppdet/modeling/backbones/blazenet.py new file mode 100644 index 0000000000000000000000000000000000000000..54c3f7e262464661f39fb73a9c5c70eabe4955c9 --- /dev/null +++ b/ppdet/modeling/backbones/blazenet.py @@ -0,0 +1,314 @@ +# 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 + +from paddle import fluid +from paddle.fluid.param_attr import ParamAttr + +from ppdet.core.workspace import register + +__all__ = ['BlazeNet'] + + +@register +class BlazeNet(object): + """ + BlazeFace, see https://arxiv.org/abs/1907.05047 + + Args: + blaze_filters (list): number of filter for each blaze block + double_blaze_filters (list): number of filter for each double_blaze block + with_extra_blocks (bool): whether or not extra blocks should be added + lite_edition (bool): whether or not is blazeface-lite + """ + + def __init__( + self, + blaze_filters=[[24, 24], [24, 24], [24, 48, 2], [48, 48], [48, 48]], + double_blaze_filters=[[48, 24, 96, 2], [96, 24, 96], [96, 24, 96], + [96, 24, 96, 2], [96, 24, 96], [96, 24, 96]], + with_extra_blocks=True, + lite_edition=False): + super(BlazeNet, self).__init__() + + self.blaze_filters = blaze_filters + self.double_blaze_filters = double_blaze_filters + self.with_extra_blocks = with_extra_blocks + self.lite_edition = lite_edition + + def __call__(self, input): + if not self.lite_edition: + conv1_num_filters = self.blaze_filters[0][0] + conv = self._conv_norm( + input=input, + num_filters=conv1_num_filters, + filter_size=3, + stride=2, + padding=1, + act='relu', + name="conv1") + + for k, v in enumerate(self.blaze_filters): + assert len(v) in [2, 3], \ + "blaze_filters {} not in [2, 3]" + if len(v) == 2: + conv = self.BlazeBlock( + conv, v[0], v[1], name='blaze_{}'.format(k)) + elif len(v) == 3: + conv = self.BlazeBlock( + conv, + v[0], + v[1], + stride=v[2], + name='blaze_{}'.format(k)) + + layers = [] + for k, v in enumerate(self.double_blaze_filters): + assert len(v) in [3, 4], \ + "blaze_filters {} not in [3, 4]" + if len(v) == 3: + conv = self.BlazeBlock( + conv, + v[0], + v[1], + double_channels=v[2], + name='double_blaze_{}'.format(k)) + elif len(v) == 4: + layers.append(conv) + conv = self.BlazeBlock( + conv, + v[0], + v[1], + double_channels=v[2], + stride=v[3], + name='double_blaze_{}'.format(k)) + layers.append(conv) + + if not self.with_extra_blocks: + return layers[-1] + return layers[-2], layers[-1] + else: + conv1 = self._conv_norm( + input=input, + num_filters=24, + filter_size=5, + stride=2, + padding=2, + act='relu', + name="conv1") + conv2 = self.Blaze_lite(conv1, 24, 24, 1, 'conv2') + conv3 = self.Blaze_lite(conv2, 24, 28, 1, 'conv3') + conv4 = self.Blaze_lite(conv3, 28, 32, 2, 'conv4') + conv5 = self.Blaze_lite(conv4, 32, 36, 1, 'conv5') + conv6 = self.Blaze_lite(conv5, 36, 42, 1, 'conv6') + conv7 = self.Blaze_lite(conv6, 42, 48, 2, 'conv7') + in_ch = 48 + for i in range(5): + conv7 = self.Blaze_lite(conv7, in_ch, in_ch + 8, 1, + 'conv{}'.format(8 + i)) + in_ch += 8 + assert in_ch == 88 + conv13 = self.Blaze_lite(conv7, 88, 96, 2, 'conv13') + for i in range(4): + conv13 = self.Blaze_lite(conv13, 96, 96, 1, + 'conv{}'.format(14 + i)) + + return conv7, conv13 + + def BlazeBlock(self, + input, + in_channels, + out_channels, + double_channels=None, + stride=1, + use_5x5kernel=True, + name=None): + assert stride in [1, 2] + use_pool = not stride == 1 + use_double_block = double_channels is not None + act = 'relu' if use_double_block else None + + if use_5x5kernel: + conv_dw = self._conv_norm( + input=input, + filter_size=5, + num_filters=in_channels, + stride=stride, + padding=2, + num_groups=in_channels, + use_cudnn=False, + name=name + "1_dw") + else: + conv_dw_1 = self._conv_norm( + input=input, + filter_size=3, + num_filters=in_channels, + stride=1, + padding=1, + num_groups=in_channels, + use_cudnn=False, + name=name + "1_dw_1") + conv_dw = self._conv_norm( + input=conv_dw_1, + filter_size=3, + num_filters=in_channels, + stride=stride, + padding=1, + num_groups=in_channels, + use_cudnn=False, + name=name + "1_dw_2") + + conv_pw = self._conv_norm( + input=conv_dw, + filter_size=1, + num_filters=out_channels, + stride=1, + padding=0, + act=act, + name=name + "1_sep") + + if use_double_block: + if use_5x5kernel: + conv_dw = self._conv_norm( + input=conv_pw, + filter_size=5, + num_filters=out_channels, + stride=1, + padding=2, + use_cudnn=False, + name=name + "2_dw") + else: + conv_dw_1 = self._conv_norm( + input=conv_pw, + filter_size=3, + num_filters=out_channels, + stride=1, + padding=1, + num_groups=out_channels, + use_cudnn=False, + name=name + "2_dw_1") + conv_dw = self._conv_norm( + input=conv_dw_1, + filter_size=3, + num_filters=out_channels, + stride=1, + padding=1, + num_groups=out_channels, + use_cudnn=False, + name=name + "2_dw_2") + + conv_pw = self._conv_norm( + input=conv_dw, + filter_size=1, + num_filters=double_channels, + stride=1, + padding=0, + name=name + "2_sep") + + # shortcut + if use_pool: + shortcut_channel = double_channels or out_channels + shortcut_pool = self._pooling_block(input, stride, stride) + channel_pad = self._conv_norm( + input=shortcut_pool, + filter_size=1, + num_filters=shortcut_channel, + stride=1, + padding=0, + name="shortcut" + name) + return fluid.layers.elementwise_add( + x=channel_pad, y=conv_pw, act='relu') + return fluid.layers.elementwise_add(x=input, y=conv_pw, act='relu') + + def Blaze_lite(self, input, in_channels, out_channels, stride=1, name=None): + assert stride in [1, 2] + use_pool = not stride == 1 + ues_pad = not in_channels == out_channels + conv_dw = self._conv_norm( + input=input, + filter_size=3, + num_filters=in_channels, + stride=stride, + padding=1, + num_groups=in_channels, + name=name + "_dw") + + conv_pw = self._conv_norm( + input=conv_dw, + filter_size=1, + num_filters=out_channels, + stride=1, + padding=0, + name=name + "_sep") + + if use_pool: + shortcut_pool = self._pooling_block(input, stride, stride) + if ues_pad: + conv_pad = shortcut_pool if use_pool else input + channel_pad = self._conv_norm( + input=conv_pad, + filter_size=1, + num_filters=out_channels, + stride=1, + padding=0, + name="shortcut" + name) + return fluid.layers.elementwise_add( + x=channel_pad, y=conv_pw, act='relu') + return fluid.layers.elementwise_add(x=input, y=conv_pw, act='relu') + + def _conv_norm( + self, + input, + filter_size, + num_filters, + stride, + padding, + num_groups=1, + act='relu', # None + use_cudnn=True, + name=None): + parameter_attr = ParamAttr( + learning_rate=0.1, + initializer=fluid.initializer.MSRA(), + name=name + "_weights") + conv = fluid.layers.conv2d( + input=input, + num_filters=num_filters, + filter_size=filter_size, + stride=stride, + padding=padding, + groups=num_groups, + act=None, + use_cudnn=use_cudnn, + param_attr=parameter_attr, + bias_attr=False) + return fluid.layers.batch_norm(input=conv, act=act) + + def _pooling_block(self, + conv, + pool_size, + pool_stride, + pool_padding=0, + ceil_mode=True): + pool = fluid.layers.pool2d( + input=conv, + pool_size=pool_size, + pool_type='max', + pool_stride=pool_stride, + pool_padding=pool_padding, + ceil_mode=ceil_mode) + return pool diff --git a/ppdet/modeling/backbones/darknet.py b/ppdet/modeling/backbones/darknet.py new file mode 100644 index 0000000000000000000000000000000000000000..37583ab29d8cd0602b0f406b52c2ce8dae1ce3e7 --- /dev/null +++ b/ppdet/modeling/backbones/darknet.py @@ -0,0 +1,170 @@ +# 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 six + +from paddle import fluid +from paddle.fluid.param_attr import ParamAttr +from paddle.fluid.regularizer import L2Decay + +from ppdet.core.workspace import register + +__all__ = ['DarkNet'] + + +@register +class DarkNet(object): + """ + DarkNet, see https://pjreddie.com/darknet/yolo/ + Args: + depth (int): network depth, currently only darknet 53 is supported + norm_type (str): normalization type, 'bn' and 'sync_bn' are supported + norm_decay (float): weight decay for normalization layer weights + """ + __shared__ = ['norm_type', 'weight_prefix_name'] + + def __init__(self, + depth=53, + norm_type='bn', + norm_decay=0., + weight_prefix_name=''): + assert depth in [53], "unsupported depth value" + self.depth = depth + self.norm_type = norm_type + self.norm_decay = norm_decay + self.depth_cfg = {53: ([1, 2, 8, 8, 4], self.basicblock)} + self.prefix_name = weight_prefix_name + + def _conv_norm(self, + input, + ch_out, + filter_size, + stride, + padding, + act='leaky', + name=None): + conv = fluid.layers.conv2d( + input=input, + num_filters=ch_out, + filter_size=filter_size, + stride=stride, + padding=padding, + act=None, + param_attr=ParamAttr(name=name + ".conv.weights"), + bias_attr=False) + + bn_name = name + ".bn" + bn_param_attr = ParamAttr( + regularizer=L2Decay(float(self.norm_decay)), + name=bn_name + '.scale') + bn_bias_attr = ParamAttr( + regularizer=L2Decay(float(self.norm_decay)), + name=bn_name + '.offset') + + out = fluid.layers.batch_norm( + input=conv, + act=None, + param_attr=bn_param_attr, + bias_attr=bn_bias_attr, + moving_mean_name=bn_name + '.mean', + moving_variance_name=bn_name + '.var') + + # leaky relu here has `alpha` as 0.1, can not be set by + # `act` param in fluid.layers.batch_norm above. + if act == 'leaky': + out = fluid.layers.leaky_relu(x=out, alpha=0.1) + + return out + + def _downsample(self, + input, + ch_out, + filter_size=3, + stride=2, + padding=1, + name=None): + return self._conv_norm( + input, + ch_out=ch_out, + filter_size=filter_size, + stride=stride, + padding=padding, + name=name) + + def basicblock(self, input, ch_out, name=None): + conv1 = self._conv_norm( + input, + ch_out=ch_out, + filter_size=1, + stride=1, + padding=0, + name=name + ".0") + conv2 = self._conv_norm( + conv1, + ch_out=ch_out * 2, + filter_size=3, + stride=1, + padding=1, + name=name + ".1") + out = fluid.layers.elementwise_add(x=input, y=conv2, act=None) + return out + + def layer_warp(self, block_func, input, ch_out, count, name=None): + out = block_func(input, ch_out=ch_out, name='{}.0'.format(name)) + for j in six.moves.xrange(1, count): + out = block_func(out, ch_out=ch_out, name='{}.{}'.format(name, j)) + return out + + def __call__(self, input): + """ + Get the backbone of DarkNet, that is output for the 5 stages. + + Args: + input (Variable): input variable. + + Returns: + The last variables of each stage. + """ + stages, block_func = self.depth_cfg[self.depth] + stages = stages[0:5] + conv = self._conv_norm( + input=input, + ch_out=32, + filter_size=3, + stride=1, + padding=1, + name=self.prefix_name + "yolo_input") + downsample_ = self._downsample( + input=conv, + ch_out=conv.shape[1] * 2, + name=self.prefix_name + "yolo_input.downsample") + blocks = [] + for i, stage in enumerate(stages): + block = self.layer_warp( + block_func=block_func, + input=downsample_, + ch_out=32 * 2**i, + count=stage, + name=self.prefix_name + "stage.{}".format(i)) + blocks.append(block) + if i < len(stages) - 1: # do not downsaple in the last stage + downsample_ = self._downsample( + input=block, + ch_out=block.shape[1] * 2, + name=self.prefix_name + "stage.{}.downsample".format(i)) + return blocks diff --git a/ppdet/modeling/backbones/faceboxnet.py b/ppdet/modeling/backbones/faceboxnet.py new file mode 100644 index 0000000000000000000000000000000000000000..0b82c86b232a9784928167f1d226eb07562821c2 --- /dev/null +++ b/ppdet/modeling/backbones/faceboxnet.py @@ -0,0 +1,364 @@ +# 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 + +from paddle import fluid +from paddle.fluid.param_attr import ParamAttr + +from ppdet.core.workspace import register + +__all__ = ['FaceBoxNet'] + + +@register +class FaceBoxNet(object): + """ + FaceBoxes, see https://https://arxiv.org/abs/1708.05234 + + Args: + with_extra_blocks (bool): whether or not extra blocks should be added + lite_edition (bool): whether or not is FaceBoxes-lite + """ + + def __init__(self, + with_extra_blocks=True, + lite_edition=False): + super(FaceBoxNet, self).__init__() + + self.with_extra_blocks = with_extra_blocks + self.lite_edition = lite_edition + + def __call__(self, input): + if self.lite_edition: + return self._simplified_edition(input) + else: + return self._original_edition(input) + + def _simplified_edition(self, input): + conv_1_1 = self._conv_norm_crelu( + input=input, + num_filters=8, + filter_size=3, + stride=2, + padding=1, + act='relu', + name="conv_1_1") + + conv_1_2 = self._conv_norm_crelu( + input=conv_1_1, + num_filters=24, + filter_size=3, + stride=2, + padding=1, + act='relu', + name="conv_1_2") + + pool1 = fluid.layers.pool2d( + input=conv_1_2, + pool_size=3, + pool_padding=1, + pool_type='avg', + name="pool_1") + + conv_2_1 = self._conv_norm( + input=pool1, + num_filters=48, + filter_size=3, + stride=2, + padding=1, + act='relu', + name="conv_2_1") + + conv_2_2 = self._conv_norm( + input=conv_2_1, + num_filters=64, + filter_size=1, + stride=1, + padding=0, + act='relu', + name="conv_2_2") + + conv_inception = conv_2_2 + + for i in range(3): + conv_inception = self._inceptionA(conv_inception, i) + + layers = [] + layers.append(conv_inception) + + conv_3_1 = self._conv_norm( + input=conv_inception, + num_filters=128, + filter_size=1, + stride=1, + padding=0, + act='relu', + name="conv_3_1") + + conv_3_2 = self._conv_norm( + input=conv_3_1, + num_filters=256, + filter_size=3, + stride=2, + padding=1, + act='relu', + name="conv_3_2") + + layers.append(conv_3_2) + + if not self.with_extra_blocks: + return layers[-1] + return layers[-2], layers[-1] + + def _original_edition(self, input): + conv_1 = self._conv_norm_crelu( + input=input, + num_filters=24, + filter_size=7, + stride=4, + padding=3, + act='relu', + name="conv_1") + + pool_1 = fluid.layers.pool2d( + input=conv_1, + pool_size=3, + pool_stride=2, + pool_padding=1, + pool_type='max', + name="pool_1") + + conv_2 = self._conv_norm_crelu( + input=pool_1, + num_filters=64, + filter_size=5, + stride=2, + padding=2, + act='relu', + name="conv_2") + + pool_2 = fluid.layers.pool2d( + input=conv_1, + pool_size=3, + pool_stride=2, + pool_padding=1, + pool_type='max', + name="pool_2") + + conv_inception = pool_2 + + for i in range(3): + conv_inception = self._inceptionA(conv_inception, i) + + layers = [] + layers.append(conv_inception) + + conv_3_1 = self._conv_norm( + input=conv_inception, + num_filters=128, + filter_size=1, + stride=1, + padding=0, + act='relu', + name="conv_3_1") + + conv_3_2 = self._conv_norm( + input=conv_3_1, + num_filters=256, + filter_size=3, + stride=2, + padding=1, + act='relu', + name="conv_3_2") + + layers.append(conv_3_2) + + conv_4_1 = self._conv_norm( + input=conv_3_2, + num_filters=128, + filter_size=1, + stride=1, + padding=0, + act='relu', + name="conv_4_1") + + conv_4_2 = self._conv_norm( + input=conv_4_1, + num_filters=256, + filter_size=3, + stride=2, + padding=1, + act='relu', + name="conv_4_2") + + layers.append(conv_4_2) + + if not self.with_extra_blocks: + return layers[-1] + + return layers[-3], layers[-2], layers[-1] + + def _conv_norm( + self, + input, + filter_size, + num_filters, + stride, + padding, + num_groups=1, + act='relu', + use_cudnn=True, + name=None): + parameter_attr = ParamAttr( + learning_rate=0.1, + initializer=fluid.initializer.MSRA(), + name=name + "_weights") + conv = fluid.layers.conv2d( + input=input, + num_filters=num_filters, + filter_size=filter_size, + stride=stride, + padding=padding, + groups=num_groups, + act=None, + use_cudnn=use_cudnn, + param_attr=parameter_attr, + bias_attr=False) + return fluid.layers.batch_norm(input=conv, act=act) + + def _conv_norm_crelu( + self, + input, + filter_size, + num_filters, + stride, + padding, + num_groups=1, + act='relu', + use_cudnn=True, + name=None): + parameter_attr = ParamAttr( + learning_rate=0.1, + initializer=fluid.initializer.MSRA(), + name=name + "_weights") + conv = fluid.layers.conv2d( + input=input, + num_filters=num_filters, + filter_size=filter_size, + stride=stride, + padding=padding, + groups=num_groups, + act=None, + use_cudnn=use_cudnn, + param_attr=parameter_attr, + bias_attr=False) + + conv_a = fluid.layers.batch_norm(input=conv, act=act) + conv_b = fluid.layers.scale(conv_a, -1) + + concat = fluid.layers.concat([conv_a, conv_b], axis=1) + + return concat + + def _pooling_block(self, + conv, + pool_size, + pool_stride, + pool_padding=0, + ceil_mode=True): + pool = fluid.layers.pool2d( + input=conv, + pool_size=pool_size, + pool_type='max', + pool_stride=pool_stride, + pool_padding=pool_padding, + ceil_mode=ceil_mode) + return pool + + def _inceptionA(self, data, idx): + idx = str(idx) + + pool1 = fluid.layers.pool2d( + input=data, + pool_size=3, + pool_padding=1, + pool_type='avg', + name='inceptionA_' + idx + '_pool1') + conv1 = self._conv_norm( + input=pool1, + filter_size=1, + num_filters=32, + stride=1, + padding=0, + act='relu', + name='inceptionA_' + idx + '_conv1') + + conv2 = self._conv_norm( + input=data, + filter_size=1, + num_filters=32, + stride=1, + padding=0, + act='relu', + name='inceptionA_' + idx + '_conv2') + + conv3 = self._conv_norm( + input=data, + filter_size=1, + num_filters=24, + stride=1, + padding=0, + act='relu', + name='inceptionA_' + idx + '_conv3_1') + conv3 = self._conv_norm( + input=conv3, + filter_size=3, + num_filters=32, + stride=1, + padding=1, + act='relu', + name='inceptionA_' + idx + '_conv3_2') + + conv4 = self._conv_norm( + input=data, + filter_size=1, + num_filters=24, + stride=1, + padding=0, + act='relu', + name='inceptionA_' + idx + '_conv4_1') + conv4 = self._conv_norm( + input=conv4, + filter_size=3, + num_filters=32, + stride=1, + padding=1, + act='relu', + name='inceptionA_' + idx + '_conv4_2') + conv4 = self._conv_norm( + input=conv4, + filter_size=3, + num_filters=32, + stride=1, + padding=1, + act='relu', + name='inceptionA_' + idx + '_conv4_3') + + concat = fluid.layers.concat( + [conv1, conv2, conv3, conv4], axis=1) + + return concat diff --git a/ppdet/modeling/backbones/fpn.py b/ppdet/modeling/backbones/fpn.py new file mode 100644 index 0000000000000000000000000000000000000000..9bd491a662dd640c62668c5878d47ea48c21d223 --- /dev/null +++ b/ppdet/modeling/backbones/fpn.py @@ -0,0 +1,216 @@ +# 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 + +from collections import OrderedDict +import copy +from paddle import fluid +from paddle.fluid.param_attr import ParamAttr +from paddle.fluid.initializer import Xavier +from paddle.fluid.regularizer import L2Decay + +from ppdet.core.workspace import register +from ppdet.modeling.ops import ConvNorm + +__all__ = ['FPN'] + + +@register +class FPN(object): + """ + Feature Pyramid Network, see https://arxiv.org/abs/1612.03144 + + Args: + num_chan (int): number of feature channels + min_level (int): lowest level of the backbone feature map to use + max_level (int): highest level of the backbone feature map to use + spatial_scale (list): feature map scaling factor + has_extra_convs (bool): whether has extral convolutions in higher levels + norm_type (str|None): normalization type, 'bn'/'sync_bn'/'affine_channel' + """ + __shared__ = ['norm_type', 'freeze_norm'] + + def __init__(self, + num_chan=256, + min_level=2, + max_level=6, + spatial_scale=[1. / 32., 1. / 16., 1. / 8., 1. / 4.], + has_extra_convs=False, + norm_type=None, + freeze_norm=False): + self.freeze_norm = freeze_norm + self.num_chan = num_chan + self.min_level = min_level + self.max_level = max_level + self.spatial_scale = spatial_scale + self.has_extra_convs = has_extra_convs + self.norm_type = norm_type + + def _add_topdown_lateral(self, body_name, body_input, upper_output): + lateral_name = 'fpn_inner_' + body_name + '_lateral' + topdown_name = 'fpn_topdown_' + body_name + fan = body_input.shape[1] + if self.norm_type: + initializer = Xavier(fan_out=fan) + lateral = ConvNorm( + body_input, + self.num_chan, + 1, + initializer=initializer, + norm_type=self.norm_type, + freeze_norm=self.freeze_norm, + name=lateral_name, + norm_name=lateral_name) + else: + lateral = fluid.layers.conv2d( + body_input, + self.num_chan, + 1, + param_attr=ParamAttr( + name=lateral_name + "_w", initializer=Xavier(fan_out=fan)), + bias_attr=ParamAttr( + name=lateral_name + "_b", + learning_rate=2., + regularizer=L2Decay(0.)), + name=lateral_name) + topdown = fluid.layers.resize_nearest( + upper_output, scale=2., name=topdown_name) + + return lateral + topdown + + def get_output(self, body_dict): + """ + Add FPN onto backbone. + + Args: + body_dict(OrderedDict): Dictionary of variables and each element is the + output of backbone. + + Return: + fpn_dict(OrderedDict): A dictionary represents the output of FPN with + their name. + spatial_scale(list): A list of multiplicative spatial scale factor. + """ + spatial_scale = copy.deepcopy(self.spatial_scale) + body_name_list = list(body_dict.keys())[::-1] + num_backbone_stages = len(body_name_list) + self.fpn_inner_output = [[] for _ in range(num_backbone_stages)] + fpn_inner_name = 'fpn_inner_' + body_name_list[0] + body_input = body_dict[body_name_list[0]] + fan = body_input.shape[1] + if self.norm_type: + initializer = Xavier(fan_out=fan) + self.fpn_inner_output[0] = ConvNorm( + body_input, + self.num_chan, + 1, + initializer=initializer, + norm_type=self.norm_type, + freeze_norm=self.freeze_norm, + name=fpn_inner_name, + norm_name=fpn_inner_name) + else: + self.fpn_inner_output[0] = fluid.layers.conv2d( + body_input, + self.num_chan, + 1, + param_attr=ParamAttr( + name=fpn_inner_name + "_w", + initializer=Xavier(fan_out=fan)), + bias_attr=ParamAttr( + name=fpn_inner_name + "_b", + learning_rate=2., + regularizer=L2Decay(0.)), + name=fpn_inner_name) + for i in range(1, num_backbone_stages): + body_name = body_name_list[i] + body_input = body_dict[body_name] + top_output = self.fpn_inner_output[i - 1] + fpn_inner_single = self._add_topdown_lateral(body_name, body_input, + top_output) + self.fpn_inner_output[i] = fpn_inner_single + fpn_dict = {} + fpn_name_list = [] + for i in range(num_backbone_stages): + fpn_name = 'fpn_' + body_name_list[i] + fan = self.fpn_inner_output[i].shape[1] * 3 * 3 + if self.norm_type: + initializer = Xavier(fan_out=fan) + fpn_output = ConvNorm( + self.fpn_inner_output[i], + self.num_chan, + 3, + initializer=initializer, + norm_type=self.norm_type, + freeze_norm=self.freeze_norm, + name=fpn_name, + norm_name=fpn_name) + else: + fpn_output = fluid.layers.conv2d( + self.fpn_inner_output[i], + self.num_chan, + filter_size=3, + padding=1, + param_attr=ParamAttr( + name=fpn_name + "_w", initializer=Xavier(fan_out=fan)), + bias_attr=ParamAttr( + name=fpn_name + "_b", + learning_rate=2., + regularizer=L2Decay(0.)), + name=fpn_name) + fpn_dict[fpn_name] = fpn_output + fpn_name_list.append(fpn_name) + if not self.has_extra_convs and self.max_level - self.min_level == len( + spatial_scale): + body_top_name = fpn_name_list[0] + body_top_extension = fluid.layers.pool2d( + fpn_dict[body_top_name], + 1, + 'max', + pool_stride=2, + name=body_top_name + '_subsampled_2x') + fpn_dict[body_top_name + '_subsampled_2x'] = body_top_extension + fpn_name_list.insert(0, body_top_name + '_subsampled_2x') + spatial_scale.insert(0, spatial_scale[0] * 0.5) + # Coarser FPN levels introduced for RetinaNet + highest_backbone_level = self.min_level + len(spatial_scale) - 1 + if self.has_extra_convs and self.max_level > highest_backbone_level: + fpn_blob = body_dict[body_name_list[0]] + for i in range(highest_backbone_level + 1, self.max_level + 1): + fpn_blob_in = fpn_blob + fpn_name = 'fpn_' + str(i) + if i > highest_backbone_level + 1: + fpn_blob_in = fluid.layers.relu(fpn_blob) + fan = fpn_blob_in.shape[1] * 3 * 3 + fpn_blob = fluid.layers.conv2d( + input=fpn_blob_in, + num_filters=self.num_chan, + filter_size=3, + stride=2, + padding=1, + param_attr=ParamAttr( + name=fpn_name + "_w", initializer=Xavier(fan_out=fan)), + bias_attr=ParamAttr( + name=fpn_name + "_b", + learning_rate=2., + regularizer=L2Decay(0.)), + name=fpn_name) + fpn_dict[fpn_name] = fpn_blob + fpn_name_list.insert(0, fpn_name) + spatial_scale.insert(0, spatial_scale[0] * 0.5) + res_dict = OrderedDict([(k, fpn_dict[k]) for k in fpn_name_list]) + return res_dict, spatial_scale diff --git a/ppdet/modeling/backbones/mobilenet.py b/ppdet/modeling/backbones/mobilenet.py new file mode 100644 index 0000000000000000000000000000000000000000..56afdf96454a994591a3d97e71b8d9619e0e68c8 --- /dev/null +++ b/ppdet/modeling/backbones/mobilenet.py @@ -0,0 +1,210 @@ +# 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 + +from paddle import fluid +from paddle.fluid.param_attr import ParamAttr +from paddle.fluid.regularizer import L2Decay + +from ppdet.core.workspace import register + +__all__ = ['MobileNet'] + + +@register +class MobileNet(object): + """ + MobileNet v1, see https://arxiv.org/abs/1704.04861 + + Args: + norm_type (str): normalization type, 'bn' and 'sync_bn' are supported + norm_decay (float): weight decay for normalization layer weights + conv_group_scale (int): scaling factor for convolution groups + with_extra_blocks (bool): if extra blocks should be added + extra_block_filters (list): number of filter for each extra block + """ + __shared__ = ['norm_type', 'weight_prefix_name'] + + def __init__(self, + norm_type='bn', + norm_decay=0., + conv_group_scale=1, + conv_learning_rate=1.0, + with_extra_blocks=False, + extra_block_filters=[[256, 512], [128, 256], [128, 256], + [64, 128]], + weight_prefix_name=''): + self.norm_type = norm_type + self.norm_decay = norm_decay + self.conv_group_scale = conv_group_scale + self.conv_learning_rate = conv_learning_rate + self.with_extra_blocks = with_extra_blocks + self.extra_block_filters = extra_block_filters + self.prefix_name = weight_prefix_name + + def _conv_norm(self, + input, + filter_size, + num_filters, + stride, + padding, + num_groups=1, + act='relu', + use_cudnn=True, + name=None): + parameter_attr = ParamAttr( + learning_rate=self.conv_learning_rate, + initializer=fluid.initializer.MSRA(), + name=name + "_weights") + conv = fluid.layers.conv2d( + input=input, + num_filters=num_filters, + filter_size=filter_size, + stride=stride, + padding=padding, + groups=num_groups, + act=None, + use_cudnn=use_cudnn, + param_attr=parameter_attr, + bias_attr=False) + + bn_name = name + "_bn" + norm_decay = self.norm_decay + bn_param_attr = ParamAttr( + regularizer=L2Decay(norm_decay), name=bn_name + '_scale') + bn_bias_attr = ParamAttr( + regularizer=L2Decay(norm_decay), name=bn_name + '_offset') + return fluid.layers.batch_norm( + input=conv, + act=act, + param_attr=bn_param_attr, + bias_attr=bn_bias_attr, + moving_mean_name=bn_name + '_mean', + moving_variance_name=bn_name + '_variance') + + def depthwise_separable(self, + input, + num_filters1, + num_filters2, + num_groups, + stride, + scale, + name=None): + depthwise_conv = self._conv_norm( + input=input, + filter_size=3, + num_filters=int(num_filters1 * scale), + stride=stride, + padding=1, + num_groups=int(num_groups * scale), + use_cudnn=False, + name=name + "_dw") + + pointwise_conv = self._conv_norm( + input=depthwise_conv, + filter_size=1, + num_filters=int(num_filters2 * scale), + stride=1, + padding=0, + name=name + "_sep") + return pointwise_conv + + def _extra_block(self, + input, + num_filters1, + num_filters2, + num_groups, + stride, + name=None): + pointwise_conv = self._conv_norm( + input=input, + filter_size=1, + num_filters=int(num_filters1), + stride=1, + num_groups=int(num_groups), + padding=0, + name=name + "_extra1") + normal_conv = self._conv_norm( + input=pointwise_conv, + filter_size=3, + num_filters=int(num_filters2), + stride=2, + num_groups=int(num_groups), + padding=1, + name=name + "_extra2") + return normal_conv + + def __call__(self, input): + scale = self.conv_group_scale + + blocks = [] + # input 1/1 + out = self._conv_norm( + input, 3, int(32 * scale), 2, 1, name=self.prefix_name + "conv1") + # 1/2 + out = self.depthwise_separable( + out, 32, 64, 32, 1, scale, name=self.prefix_name + "conv2_1") + out = self.depthwise_separable( + out, 64, 128, 64, 2, scale, name=self.prefix_name + "conv2_2") + # 1/4 + out = self.depthwise_separable( + out, 128, 128, 128, 1, scale, name=self.prefix_name + "conv3_1") + out = self.depthwise_separable( + out, 128, 256, 128, 2, scale, name=self.prefix_name + "conv3_2") + # 1/8 + blocks.append(out) + out = self.depthwise_separable( + out, 256, 256, 256, 1, scale, name=self.prefix_name + "conv4_1") + out = self.depthwise_separable( + out, 256, 512, 256, 2, scale, name=self.prefix_name + "conv4_2") + # 1/16 + blocks.append(out) + for i in range(5): + out = self.depthwise_separable( + out, + 512, + 512, + 512, + 1, + scale, + name=self.prefix_name + "conv5_" + str(i + 1)) + module11 = out + + out = self.depthwise_separable( + out, 512, 1024, 512, 2, scale, name=self.prefix_name + "conv5_6") + # 1/32 + out = self.depthwise_separable( + out, 1024, 1024, 1024, 1, scale, name=self.prefix_name + "conv6") + module13 = out + blocks.append(out) + if not self.with_extra_blocks: + return blocks + + num_filters = self.extra_block_filters + module14 = self._extra_block(module13, num_filters[0][0], + num_filters[0][1], 1, 2, + self.prefix_name + "conv7_1") + module15 = self._extra_block(module14, num_filters[1][0], + num_filters[1][1], 1, 2, + self.prefix_name + "conv7_2") + module16 = self._extra_block(module15, num_filters[2][0], + num_filters[2][1], 1, 2, + self.prefix_name + "conv7_3") + module17 = self._extra_block(module16, num_filters[3][0], + num_filters[3][1], 1, 2, + self.prefix_name + "conv7_4") + return module11, module13, module14, module15, module16, module17 diff --git a/ppdet/modeling/backbones/name_adapter.py b/ppdet/modeling/backbones/name_adapter.py new file mode 100644 index 0000000000000000000000000000000000000000..2cb16d0c9cc82d9c07988ae47c6347973add0dc8 --- /dev/null +++ b/ppdet/modeling/backbones/name_adapter.py @@ -0,0 +1,73 @@ +# 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. + + +class NameAdapter(object): + """Fix the backbones variable names for pretrained weight""" + + def __init__(self, model): + super(NameAdapter, self).__init__() + self.model = model + + @property + def model_type(self): + return getattr(self.model, '_model_type', '') + + @property + def variant(self): + return getattr(self.model, 'variant', '') + + def fix_conv_norm_name(self, name): + if name == "conv1": + bn_name = "bn_" + name + else: + bn_name = "bn" + name[3:] + # the naming rule is same as pretrained weight + if self.model_type == 'SEResNeXt': + bn_name = name + "_bn" + return bn_name + + def fix_shortcut_name(self, name): + if self.model_type == 'SEResNeXt': + name = 'conv' + name + '_prj' + return name + + def fix_bottleneck_name(self, name): + if self.model_type == 'SEResNeXt': + conv_name1 = 'conv' + name + '_x1' + conv_name2 = 'conv' + name + '_x2' + conv_name3 = 'conv' + name + '_x3' + shortcut_name = name + else: + conv_name1 = name + "_branch2a" + conv_name2 = name + "_branch2b" + conv_name3 = name + "_branch2c" + shortcut_name = name + "_branch1" + return conv_name1, conv_name2, conv_name3, shortcut_name + + def fix_layer_warp_name(self, stage_num, count, i): + name = 'res' + str(stage_num) + if count > 10 and stage_num == 4: + if i == 0: + conv_name = name + "a" + else: + conv_name = name + "b" + str(i) + else: + conv_name = name + chr(ord("a") + i) + if self.model_type == 'SEResNeXt': + conv_name = str(stage_num + 2) + '_' + str(i + 1) + return conv_name + + def fix_c1_stage_name(self): + return "res_conv1" if self.model_type == 'ResNeXt' else "conv1" diff --git a/ppdet/modeling/backbones/resnet.py b/ppdet/modeling/backbones/resnet.py new file mode 100644 index 0000000000000000000000000000000000000000..496f5e76cbec09639c63b3b5d6499ee0d6f32598 --- /dev/null +++ b/ppdet/modeling/backbones/resnet.py @@ -0,0 +1,431 @@ +# 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 + +from collections import OrderedDict + +from paddle import fluid +from paddle.fluid.param_attr import ParamAttr +from paddle.fluid.framework import Variable +from paddle.fluid.regularizer import L2Decay +from paddle.fluid.initializer import Constant + +from ppdet.core.workspace import register, serializable +from numbers import Integral + +from .name_adapter import NameAdapter + +__all__ = ['ResNet', 'ResNetC5'] + + +@register +@serializable +class ResNet(object): + """ + Residual Network, see https://arxiv.org/abs/1512.03385 + Args: + depth (int): ResNet depth, should be 18, 34, 50, 101, 152. + freeze_at (int): freeze the backbone at which stage + norm_type (str): normalization type, 'bn'/'sync_bn'/'affine_channel' + freeze_norm (bool): freeze normalization layers + norm_decay (float): weight decay for normalization layer weights + variant (str): ResNet variant, supports 'a', 'b', 'c', 'd' currently + feature_maps (list): index of stages whose feature maps are returned + dcn_v2_stages (list): index of stages who select deformable conv v2 + """ + __shared__ = ['norm_type', 'freeze_norm', 'weight_prefix_name'] + + def __init__(self, + depth=50, + freeze_at=2, + norm_type='affine_channel', + freeze_norm=True, + norm_decay=0., + variant='b', + feature_maps=[2, 3, 4, 5], + dcn_v2_stages=[], + weight_prefix_name=''): + super(ResNet, self).__init__() + + if isinstance(feature_maps, Integral): + feature_maps = [feature_maps] + + assert depth in [18, 34, 50, 101, 152], \ + "depth {} not in [18, 34, 50, 101, 152]" + assert variant in ['a', 'b', 'c', 'd'], "invalid ResNet variant" + assert 0 <= freeze_at <= 4, "freeze_at should be 0, 1, 2, 3 or 4" + assert len(feature_maps) > 0, "need one or more feature maps" + assert norm_type in ['bn', 'sync_bn', 'affine_channel'] + + self.depth = depth + self.freeze_at = freeze_at + self.norm_type = norm_type + self.norm_decay = norm_decay + self.freeze_norm = freeze_norm + self.variant = variant + self._model_type = 'ResNet' + self.feature_maps = feature_maps + self.dcn_v2_stages = dcn_v2_stages + self.depth_cfg = { + 18: ([2, 2, 2, 2], self.basicblock), + 34: ([3, 4, 6, 3], self.basicblock), + 50: ([3, 4, 6, 3], self.bottleneck), + 101: ([3, 4, 23, 3], self.bottleneck), + 152: ([3, 8, 36, 3], self.bottleneck) + } + self.stage_filters = [64, 128, 256, 512] + self._c1_out_chan_num = 64 + self.na = NameAdapter(self) + self.prefix_name = weight_prefix_name + + def _conv_offset(self, + input, + filter_size, + stride, + padding, + act=None, + name=None): + out_channel = filter_size * filter_size * 3 + out = fluid.layers.conv2d( + input, + num_filters=out_channel, + filter_size=filter_size, + stride=stride, + padding=padding, + param_attr=ParamAttr( + initializer=Constant(0.0), name=name + ".w_0"), + bias_attr=ParamAttr( + initializer=Constant(0.0), name=name + ".b_0"), + act=act, + name=name) + return out + + def _conv_norm(self, + input, + num_filters, + filter_size, + stride=1, + groups=1, + act=None, + name=None, + dcn_v2=False): + _name = self.prefix_name + name if self.prefix_name != '' else name + if not dcn_v2: + conv = fluid.layers.conv2d( + input=input, + num_filters=num_filters, + filter_size=filter_size, + stride=stride, + padding=(filter_size - 1) // 2, + groups=groups, + act=None, + param_attr=ParamAttr(name=_name + "_weights"), + bias_attr=False, + name=_name + '.conv2d.output.1') + else: + # select deformable conv" + offset_mask = self._conv_offset( + input=input, + filter_size=filter_size, + stride=stride, + padding=(filter_size - 1) // 2, + act=None, + name=_name + "_conv_offset") + offset_channel = filter_size**2 * 2 + mask_channel = filter_size**2 + offset, mask = fluid.layers.split( + input=offset_mask, + num_or_sections=[offset_channel, mask_channel], + dim=1) + mask = fluid.layers.sigmoid(mask) + conv = fluid.layers.deformable_conv( + input=input, + offset=offset, + mask=mask, + num_filters=num_filters, + filter_size=filter_size, + stride=stride, + padding=(filter_size - 1) // 2, + groups=groups, + deformable_groups=1, + im2col_step=1, + param_attr=ParamAttr(name=_name + "_weights"), + bias_attr=False, + name=_name + ".conv2d.output.1") + + bn_name = self.na.fix_conv_norm_name(name) + bn_name = self.prefix_name + bn_name if self.prefix_name != '' else bn_name + + norm_lr = 0. if self.freeze_norm else 1. + norm_decay = self.norm_decay + pattr = ParamAttr( + name=bn_name + '_scale', + learning_rate=norm_lr, + regularizer=L2Decay(norm_decay)) + battr = ParamAttr( + name=bn_name + '_offset', + learning_rate=norm_lr, + regularizer=L2Decay(norm_decay)) + + if self.norm_type in ['bn', 'sync_bn']: + global_stats = True if self.freeze_norm else False + out = fluid.layers.batch_norm( + input=conv, + act=act, + name=bn_name + '.output.1', + param_attr=pattr, + bias_attr=battr, + moving_mean_name=bn_name + '_mean', + moving_variance_name=bn_name + '_variance', + use_global_stats=global_stats) + scale = fluid.framework._get_var(pattr.name) + bias = fluid.framework._get_var(battr.name) + elif self.norm_type == 'affine_channel': + scale = fluid.layers.create_parameter( + shape=[conv.shape[1]], + dtype=conv.dtype, + attr=pattr, + default_initializer=fluid.initializer.Constant(1.)) + bias = fluid.layers.create_parameter( + shape=[conv.shape[1]], + dtype=conv.dtype, + attr=battr, + default_initializer=fluid.initializer.Constant(0.)) + out = fluid.layers.affine_channel( + x=conv, scale=scale, bias=bias, act=act) + if self.freeze_norm: + scale.stop_gradient = True + bias.stop_gradient = True + return out + + def _shortcut(self, input, ch_out, stride, is_first, name): + max_pooling_in_short_cut = self.variant == 'd' + ch_in = input.shape[1] + # the naming rule is same as pretrained weight + name = self.na.fix_shortcut_name(name) + std_senet = getattr(self, 'std_senet', False) + if ch_in != ch_out or stride != 1 or (self.depth < 50 and is_first): + if std_senet: + if is_first: + return self._conv_norm(input, ch_out, 1, stride, name=name) + else: + return self._conv_norm(input, ch_out, 3, stride, name=name) + if max_pooling_in_short_cut and not is_first: + input = fluid.layers.pool2d( + input=input, + pool_size=2, + pool_stride=2, + pool_padding=0, + ceil_mode=True, + pool_type='avg') + return self._conv_norm(input, ch_out, 1, 1, name=name) + return self._conv_norm(input, ch_out, 1, stride, name=name) + else: + return input + + def bottleneck(self, + input, + num_filters, + stride, + is_first, + name, + dcn_v2=False): + if self.variant == 'a': + stride1, stride2 = stride, 1 + else: + stride1, stride2 = 1, stride + + # ResNeXt + groups = getattr(self, 'groups', 1) + group_width = getattr(self, 'group_width', -1) + if groups == 1: + expand = 4 + elif (groups * group_width) == 256: + expand = 1 + else: # FIXME hard code for now, handles 32x4d, 64x4d and 32x8d + num_filters = num_filters // 2 + expand = 2 + + conv_name1, conv_name2, conv_name3, \ + shortcut_name = self.na.fix_bottleneck_name(name) + std_senet = getattr(self, 'std_senet', False) + if std_senet: + conv_def = [ + [int(num_filters / 2), 1, stride1, 'relu', 1, conv_name1], + [num_filters, 3, stride2, 'relu', groups, conv_name2], + [num_filters * expand, 1, 1, None, 1, conv_name3] + ] + else: + conv_def = [[num_filters, 1, stride1, 'relu', 1, conv_name1], + [num_filters, 3, stride2, 'relu', groups, conv_name2], + [num_filters * expand, 1, 1, None, 1, conv_name3]] + + residual = input + for i, (c, k, s, act, g, _name) in enumerate(conv_def): + residual = self._conv_norm( + input=residual, + num_filters=c, + filter_size=k, + stride=s, + act=act, + groups=g, + name=_name, + dcn_v2=(i == 1 and dcn_v2)) + short = self._shortcut( + input, + num_filters * expand, + stride, + is_first=is_first, + name=shortcut_name) + # Squeeze-and-Excitation + if callable(getattr(self, '_squeeze_excitation', None)): + residual = self._squeeze_excitation( + input=residual, num_channels=num_filters, name='fc' + name) + return fluid.layers.elementwise_add( + x=short, y=residual, act='relu', name=name + ".add.output.5") + + def basicblock(self, + input, + num_filters, + stride, + is_first, + name, + dcn_v2=False): + assert dcn_v2 is False, "Not implemented yet." + conv0 = self._conv_norm( + input=input, + num_filters=num_filters, + filter_size=3, + act='relu', + stride=stride, + name=name + "_branch2a") + conv1 = self._conv_norm( + input=conv0, + num_filters=num_filters, + filter_size=3, + act=None, + name=name + "_branch2b") + short = self._shortcut( + input, num_filters, stride, is_first, name=name + "_branch1") + return fluid.layers.elementwise_add(x=short, y=conv1, act='relu') + + def layer_warp(self, input, stage_num): + """ + Args: + input (Variable): input variable. + stage_num (int): the stage number, should be 2, 3, 4, 5 + + Returns: + The last variable in endpoint-th stage. + """ + assert stage_num in [2, 3, 4, 5] + + stages, block_func = self.depth_cfg[self.depth] + count = stages[stage_num - 2] + + ch_out = self.stage_filters[stage_num - 2] + is_first = False if stage_num != 2 else True + dcn_v2 = True if stage_num in self.dcn_v2_stages else False + # Make the layer name and parameter name consistent + # with ImageNet pre-trained model + conv = input + for i in range(count): + conv_name = self.na.fix_layer_warp_name(stage_num, count, i) + if self.depth < 50: + is_first = True if i == 0 and stage_num == 2 else False + conv = block_func( + input=conv, + num_filters=ch_out, + stride=2 if i == 0 and stage_num != 2 else 1, + is_first=is_first, + name=conv_name, + dcn_v2=dcn_v2) + return conv + + def c1_stage(self, input): + out_chan = self._c1_out_chan_num + + conv1_name = self.na.fix_c1_stage_name() + + if self.variant in ['c', 'd']: + conv_def = [ + [out_chan // 2, 3, 2, "conv1_1"], + [out_chan // 2, 3, 1, "conv1_2"], + [out_chan, 3, 1, "conv1_3"], + ] + else: + conv_def = [[out_chan, 7, 2, conv1_name]] + + for (c, k, s, _name) in conv_def: + input = self._conv_norm( + input=input, + num_filters=c, + filter_size=k, + stride=s, + act='relu', + name=_name) + + output = fluid.layers.pool2d( + input=input, + pool_size=3, + pool_stride=2, + pool_padding=1, + pool_type='max') + return output + + def __call__(self, input): + assert isinstance(input, Variable) + assert not (set(self.feature_maps) - set([2, 3, 4, 5])), \ + "feature maps {} not in [2, 3, 4, 5]".format(self.feature_maps) + + res_endpoints = [] + + res = input + feature_maps = self.feature_maps + severed_head = getattr(self, 'severed_head', False) + if not severed_head: + res = self.c1_stage(res) + feature_maps = range(2, max(self.feature_maps) + 1) + + for i in feature_maps: + res = self.layer_warp(res, i) + if i in self.feature_maps: + res_endpoints.append(res) + if self.freeze_at >= i: + res.stop_gradient = True + + return OrderedDict([('res{}_sum'.format(self.feature_maps[idx]), feat) + for idx, feat in enumerate(res_endpoints)]) + + +@register +@serializable +class ResNetC5(ResNet): + __doc__ = ResNet.__doc__ + + def __init__(self, + depth=50, + freeze_at=2, + norm_type='affine_channel', + freeze_norm=True, + norm_decay=0., + variant='b', + feature_maps=[5], + weight_prefix_name=''): + super(ResNetC5, self).__init__(depth, freeze_at, norm_type, freeze_norm, + norm_decay, variant, feature_maps) + self.severed_head = True diff --git a/ppdet/modeling/backbones/resnext.py b/ppdet/modeling/backbones/resnext.py new file mode 100644 index 0000000000000000000000000000000000000000..5452511376de21ccca731b0f4e08b38552fb4e9d --- /dev/null +++ b/ppdet/modeling/backbones/resnext.py @@ -0,0 +1,89 @@ +# 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 + +from ppdet.core.workspace import register, serializable +from .resnet import ResNet + +__all__ = ['ResNeXt'] + + +@register +@serializable +class ResNeXt(ResNet): + """ + ResNeXt, see https://arxiv.org/abs/1611.05431 + Args: + depth (int): network depth, should be 50, 101, 152. + groups (int): group convolution cardinality + group_width (int): width of each group convolution + freeze_at (int): freeze the backbone at which stage + norm_type (str): normalization type, 'bn', 'sync_bn' or 'affine_channel' + freeze_norm (bool): freeze normalization layers + norm_decay (float): weight decay for normalization layer weights + variant (str): ResNet variant, supports 'a', 'b', 'c', 'd' currently + feature_maps (list): index of the stages whose feature maps are returned + dcn_v2_stages (list): index of stages who select deformable conv v2 + """ + + def __init__(self, + depth=50, + groups=64, + group_width=4, + freeze_at=2, + norm_type='affine_channel', + freeze_norm=True, + norm_decay=True, + variant='a', + feature_maps=[2, 3, 4, 5], + dcn_v2_stages=[], + weight_prefix_name=''): + assert depth in [50, 101, 152], "depth {} should be 50, 101 or 152" + super(ResNeXt, self).__init__(depth, freeze_at, norm_type, freeze_norm, + norm_decay, variant, feature_maps) + self.depth_cfg = { + 50: ([3, 4, 6, 3], self.bottleneck), + 101: ([3, 4, 23, 3], self.bottleneck), + 152: ([3, 8, 36, 3], self.bottleneck) + } + self.stage_filters = [256, 512, 1024, 2048] + self.groups = groups + self.group_width = group_width + self._model_type = 'ResNeXt' + self.dcn_v2_stages = dcn_v2_stages + + +@register +@serializable +class ResNeXtC5(ResNeXt): + __doc__ = ResNeXt.__doc__ + + def __init__(self, + depth=50, + groups=64, + group_width=4, + freeze_at=2, + norm_type='affine_channel', + freeze_norm=True, + norm_decay=True, + variant='a', + feature_maps=[5], + weight_prefix_name=''): + super(ResNeXtC5, self).__init__(depth, groups, group_width, freeze_at, + norm_type, freeze_norm, norm_decay, + variant, feature_maps) + self.severed_head = True diff --git a/ppdet/modeling/backbones/senet.py b/ppdet/modeling/backbones/senet.py new file mode 100644 index 0000000000000000000000000000000000000000..09c69ff199f03837b979127664d7e752db7e587a --- /dev/null +++ b/ppdet/modeling/backbones/senet.py @@ -0,0 +1,122 @@ +# 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 math + +from paddle import fluid +from paddle.fluid.param_attr import ParamAttr + +from ppdet.core.workspace import register, serializable +from .resnext import ResNeXt + +__all__ = ['SENet', 'SENetC5'] + + +@register +@serializable +class SENet(ResNeXt): + """ + Squeeze-and-Excitation Networks, see https://arxiv.org/abs/1709.01507 + Args: + depth (int): SENet depth, should be 50, 101, 152 + groups (int): group convolution cardinality + group_width (int): width of each group convolution + freeze_at (int): freeze the backbone at which stage + norm_type (str): normalization type, 'bn', 'sync_bn' or 'affine_channel' + freeze_norm (bool): freeze normalization layers + norm_decay (float): weight decay for normalization layer weights + variant (str): ResNet variant, supports 'a', 'b', 'c', 'd' currently + feature_maps (list): index of the stages whose feature maps are returned + dcn_v2_stages (list): index of stages who select deformable conv v2 + """ + + def __init__(self, + depth=50, + groups=64, + group_width=4, + freeze_at=2, + norm_type='affine_channel', + freeze_norm=True, + norm_decay=0., + variant='d', + feature_maps=[2, 3, 4, 5], + dcn_v2_stages=[], + std_senet=False, + weight_prefix_name=''): + super(SENet, self).__init__(depth, groups, group_width, freeze_at, + norm_type, freeze_norm, norm_decay, variant, + feature_maps) + if depth < 152: + self.stage_filters = [128, 256, 512, 1024] + else: + self.stage_filters = [256, 512, 1024, 2048] + self.reduction_ratio = 16 + self.std_senet = std_senet + self._c1_out_chan_num = 128 + self._model_type = 'SEResNeXt' + self.dcn_v2_stages = dcn_v2_stages + + def _squeeze_excitation(self, input, num_channels, name=None): + pool = fluid.layers.pool2d( + input=input, + pool_size=0, + pool_type='avg', + global_pooling=True, + use_cudnn=False) + stdv = 1.0 / math.sqrt(pool.shape[1] * 1.0) + squeeze = fluid.layers.fc( + input=pool, + size=int(num_channels / self.reduction_ratio), + act='relu', + param_attr=fluid.param_attr.ParamAttr( + initializer=fluid.initializer.Uniform(-stdv, stdv), + name=name + '_sqz_weights'), + bias_attr=ParamAttr(name=name + '_sqz_offset')) + stdv = 1.0 / math.sqrt(squeeze.shape[1] * 1.0) + excitation = fluid.layers.fc( + input=squeeze, + size=num_channels, + act='sigmoid', + param_attr=fluid.param_attr.ParamAttr( + initializer=fluid.initializer.Uniform(-stdv, stdv), + name=name + '_exc_weights'), + bias_attr=ParamAttr(name=name + '_exc_offset')) + scale = fluid.layers.elementwise_mul(x=input, y=excitation, axis=0) + return scale + + +@register +@serializable +class SENetC5(SENet): + __doc__ = SENet.__doc__ + + def __init__(self, + depth=50, + groups=64, + group_width=4, + freeze_at=2, + norm_type='affine_channel', + freeze_norm=True, + norm_decay=0., + variant='d', + feature_maps=[5], + weight_prefix_name=''): + super(SENetC5, self).__init__(depth, groups, group_width, freeze_at, + norm_type, freeze_norm, norm_decay, + variant, feature_maps) + self.severed_head = True diff --git a/ppdet/modeling/backbones/vgg.py b/ppdet/modeling/backbones/vgg.py new file mode 100644 index 0000000000000000000000000000000000000000..28bd29272773dd670e0ef7223814a25784c54bf1 --- /dev/null +++ b/ppdet/modeling/backbones/vgg.py @@ -0,0 +1,200 @@ +# 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 + +from paddle import fluid +from paddle.fluid.param_attr import ParamAttr + +from ppdet.core.workspace import register + +__all__ = ['VGG'] + + +@register +class VGG(object): + """ + VGG, see https://arxiv.org/abs/1409.1556 + + Args: + depth (int): the VGG net depth (16 or 19) + normalizations (list): params list of init scale in l2 norm, skip init + scale if param is -1. + with_extra_blocks (bool): whether or not extra blocks should be added + extra_block_filters (list): in each extra block, params: + [in_channel, out_channel, padding_size, stride_size, filter_size] + """ + + def __init__(self, + depth=16, + with_extra_blocks=False, + normalizations=[20., -1, -1, -1, -1, -1], + extra_block_filters=[[256, 512, 1, 2, 3], [128, 256, 1, 2, 3], + [128, 256, 0, 1, 3], [128, 256, 0, 1, 3]]): + assert depth in [16, 19], \ + "depth {} not in [16, 19]" + + self.depth = depth + self.depth_cfg = { + 16: [2, 2, 3, 3, 3], + 19: [2, 2, 4, 4, 4] + } + self.with_extra_blocks = with_extra_blocks + self.normalizations = normalizations + self.extra_block_filters = extra_block_filters + + def __call__(self, input): + layers = [] + layers += self._vgg_block(input) + + if not self.with_extra_blocks: + return layers[-1] + + layers += self._add_extras_block(layers[-1]) + norm_cfg = self.normalizations + for k, v in enumerate(layers): + if not norm_cfg[k] == -1: + layers[k] = self._l2_norm_scale(v, init_scale=norm_cfg[k]) + + return layers + + def _vgg_block(self, input): + nums = self.depth_cfg[self.depth] + vgg_base = [64, 128, 256, 512, 512] + conv = input + layers = [] + for k, v in enumerate(vgg_base): + conv = self._conv_block(conv, v, nums[k], name="conv{}_".format(k + 1)) + layers.append(conv) + if k == 4: + conv = self._pooling_block(conv, 3, 1, pool_padding=1) + else: + conv = self._pooling_block(conv, 2, 2) + + fc6 = self._conv_layer(conv, 1024, 3, 1, 6, dilation=6, name="fc6") + fc7 = self._conv_layer(fc6, 1024, 1, 1, 0, name="fc7") + + return [layers[3], fc7] + + def _add_extras_block(self, input): + cfg = self.extra_block_filters + conv = input + layers = [] + for k, v in enumerate(cfg): + assert len(v) == 5, "extra_block_filters size not fix" + conv = self._extra_block(conv, v[0], v[1], + v[2], v[3], v[4], name="conv{}_".format(6 + k)) + layers.append(conv) + + return layers + + def _conv_block(self, input, num_filter, groups, name=None): + conv = input + for i in range(groups): + conv = self._conv_layer( + input=conv, + num_filters=num_filter, + filter_size=3, + stride=1, + padding=1, + act='relu', + name=name + str(i + 1)) + return conv + + def _extra_block(self, + input, + num_filters1, + num_filters2, + padding_size, + stride_size, + filter_size, + name=None): + # 1x1 conv + conv_1 = self._conv_layer( + input=input, + num_filters=int(num_filters1), + filter_size=1, + stride=1, + act='relu', + padding=0, + name=name + "1") + + # 3x3 conv + conv_2 = self._conv_layer( + input=conv_1, + num_filters=int(num_filters2), + filter_size=filter_size, + stride=stride_size, + act='relu', + padding=padding_size, + name=name + "2") + return conv_2 + + def _conv_layer(self, + input, + num_filters, + filter_size, + stride, + padding, + dilation=1, + act='relu', + use_cudnn=True, + name=None): + conv = fluid.layers.conv2d( + input=input, + num_filters=num_filters, + filter_size=filter_size, + stride=stride, + padding=padding, + dilation=dilation, + act=act, + use_cudnn=use_cudnn, + param_attr=ParamAttr(name=name + "_weights"), + bias_attr=ParamAttr(name=name + "_biases"), + name=name + '.conv2d.output.1') + return conv + + def _pooling_block(self, + conv, + pool_size, + pool_stride, + pool_padding=0, + ceil_mode=True): + pool = fluid.layers.pool2d( + input=conv, + pool_size=pool_size, + pool_type='max', + pool_stride=pool_stride, + pool_padding=pool_padding, + ceil_mode=ceil_mode) + return pool + + def _l2_norm_scale(self, input, init_scale=1.0, channel_shared=False): + from paddle.fluid.layer_helper import LayerHelper + from paddle.fluid.initializer import Constant + helper = LayerHelper("Scale") + l2_norm = fluid.layers.l2_normalize( + input, axis=1) # l2 norm along channel + shape = [1] if channel_shared else [input.shape[1]] + scale = helper.create_parameter( + attr=helper.param_attr, + shape=shape, + dtype=input.dtype, + default_initializer=Constant(init_scale)) + out = fluid.layers.elementwise_mul( + x=l2_norm, y=scale, axis=-1 if channel_shared else 1, + name="conv4_3_norm_scale") + return out diff --git a/ppdet/modeling/model_input.py b/ppdet/modeling/model_input.py new file mode 100644 index 0000000000000000000000000000000000000000..6b407cfbd82d1588cbc020b3e8bf5e7d5f4b930c --- /dev/null +++ b/ppdet/modeling/model_input.py @@ -0,0 +1,129 @@ +# 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, use_pyreader=True, 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]) + + pyreader = None + if use_pyreader: + pyreader = fluid.io.PyReader( + feed_list=list(feed_vars.values()), + capacity=64, + use_double_buffer=True, + iterable=False) + return pyreader, feed_vars diff --git a/ppdet/modeling/ops.py b/ppdet/modeling/ops.py new file mode 100644 index 0000000000000000000000000000000000000000..1312f86b49077536043ceb96cbca0f6a1c9b9ef3 --- /dev/null +++ b/ppdet/modeling/ops.py @@ -0,0 +1,349 @@ +# 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 numbers import Integral + +from paddle import fluid +from paddle.fluid.param_attr import ParamAttr +from paddle.fluid.regularizer import L2Decay +from ppdet.core.workspace import register, serializable + +__all__ = [ + 'AnchorGenerator', 'RPNTargetAssign', 'GenerateProposals', 'MultiClassNMS', + 'BBoxAssigner', 'MaskAssigner', 'RoIAlign', 'RoIPool', 'MultiBoxHead', + 'SSDOutputDecoder', 'RetinaTargetAssign', 'RetinaOutputDecoder', 'ConvNorm' +] + + +def ConvNorm(input, + num_filters, + filter_size, + stride=1, + groups=1, + norm_decay=0., + norm_type='affine_channel', + norm_groups=32, + dilation=1, + lr_scale=1, + freeze_norm=False, + act=None, + norm_name=None, + initializer=None, + name=None): + fan = num_filters + conv = fluid.layers.conv2d( + input=input, + num_filters=num_filters, + filter_size=filter_size, + stride=stride, + padding=((filter_size - 1) // 2) * dilation, + dilation=dilation, + groups=groups, + act=None, + param_attr=ParamAttr( + name=name + "_weights", + initializer=initializer, + learning_rate=lr_scale), + bias_attr=False, + name=name + '.conv2d.output.1') + + norm_lr = 0. if freeze_norm else 1. + pattr = ParamAttr( + name=norm_name + '_scale', + learning_rate=norm_lr * lr_scale, + regularizer=L2Decay(norm_decay)) + battr = ParamAttr( + name=norm_name + '_offset', + learning_rate=norm_lr * lr_scale, + regularizer=L2Decay(norm_decay)) + + if norm_type in ['bn', 'sync_bn']: + global_stats = True if freeze_norm else False + out = fluid.layers.batch_norm( + input=conv, + act=act, + name=norm_name + '.output.1', + param_attr=pattr, + bias_attr=battr, + moving_mean_name=norm_name + '_mean', + moving_variance_name=norm_name + '_variance', + use_global_stats=global_stats) + scale = fluid.framework._get_var(pattr.name) + bias = fluid.framework._get_var(battr.name) + elif norm_type == 'gn': + out = fluid.layers.group_norm( + input=conv, + act=act, + name=norm_name + '.output.1', + groups=norm_groups, + param_attr=pattr, + bias_attr=battr) + scale = fluid.framework._get_var(pattr.name) + bias = fluid.framework._get_var(battr.name) + elif norm_type == 'affine_channel': + scale = fluid.layers.create_parameter( + shape=[conv.shape[1]], + dtype=conv.dtype, + attr=pattr, + default_initializer=fluid.initializer.Constant(1.)) + bias = fluid.layers.create_parameter( + shape=[conv.shape[1]], + dtype=conv.dtype, + attr=battr, + default_initializer=fluid.initializer.Constant(0.)) + out = fluid.layers.affine_channel( + x=conv, scale=scale, bias=bias, act=act) + if freeze_norm: + scale.stop_gradient = True + bias.stop_gradient = True + return out + + +@register +@serializable +class AnchorGenerator(object): + __op__ = fluid.layers.anchor_generator + __append_doc__ = True + + def __init__(self, + stride=[16.0, 16.0], + anchor_sizes=[32, 64, 128, 256, 512], + aspect_ratios=[0.5, 1., 2.], + variance=[1., 1., 1., 1.]): + super(AnchorGenerator, self).__init__() + self.anchor_sizes = anchor_sizes + self.aspect_ratios = aspect_ratios + self.variance = variance + self.stride = stride + + +@register +@serializable +class RPNTargetAssign(object): + __op__ = fluid.layers.rpn_target_assign + __append_doc__ = True + + def __init__(self, + rpn_batch_size_per_im=256, + rpn_straddle_thresh=0., + rpn_fg_fraction=0.5, + rpn_positive_overlap=0.7, + rpn_negative_overlap=0.3, + use_random=True): + super(RPNTargetAssign, self).__init__() + self.rpn_batch_size_per_im = rpn_batch_size_per_im + self.rpn_straddle_thresh = rpn_straddle_thresh + self.rpn_fg_fraction = rpn_fg_fraction + self.rpn_positive_overlap = rpn_positive_overlap + self.rpn_negative_overlap = rpn_negative_overlap + self.use_random = use_random + + +@register +@serializable +class GenerateProposals(object): + __op__ = fluid.layers.generate_proposals + __append_doc__ = True + + def __init__(self, + pre_nms_top_n=6000, + post_nms_top_n=1000, + nms_thresh=.5, + min_size=.1, + eta=1.): + super(GenerateProposals, self).__init__() + self.pre_nms_top_n = pre_nms_top_n + self.post_nms_top_n = post_nms_top_n + self.nms_thresh = nms_thresh + self.min_size = min_size + self.eta = eta + + +@register +class MaskAssigner(object): + __op__ = fluid.layers.generate_mask_labels + __append_doc__ = True + __shared__ = ['num_classes'] + + def __init__(self, num_classes=81, resolution=14): + super(MaskAssigner, self).__init__() + self.num_classes = num_classes + self.resolution = resolution + + +@register +@serializable +class MultiClassNMS(object): + __op__ = fluid.layers.multiclass_nms + __append_doc__ = True + + def __init__(self, + score_threshold=.05, + nms_top_k=-1, + keep_top_k=100, + nms_threshold=.5, + normalized=False, + nms_eta=1.0, + background_label=0): + super(MultiClassNMS, self).__init__() + self.score_threshold = score_threshold + self.nms_top_k = nms_top_k + self.keep_top_k = keep_top_k + self.nms_threshold = nms_threshold + self.normalized = normalized + self.nms_eta = nms_eta + self.background_label = background_label + + +@register +class BBoxAssigner(object): + __op__ = fluid.layers.generate_proposal_labels + __append_doc__ = True + __shared__ = ['num_classes'] + + def __init__(self, + batch_size_per_im=512, + fg_fraction=.25, + fg_thresh=.5, + bg_thresh_hi=.5, + bg_thresh_lo=0., + bbox_reg_weights=[0.1, 0.1, 0.2, 0.2], + num_classes=81, + shuffle_before_sample=True): + super(BBoxAssigner, self).__init__() + self.batch_size_per_im = batch_size_per_im + self.fg_fraction = fg_fraction + self.fg_thresh = fg_thresh + self.bg_thresh_hi = bg_thresh_hi + self.bg_thresh_lo = bg_thresh_lo + self.bbox_reg_weights = bbox_reg_weights + self.class_nums = num_classes + self.use_random = shuffle_before_sample + + +@register +class RoIAlign(object): + __op__ = fluid.layers.roi_align + __append_doc__ = True + + def __init__(self, resolution=7, spatial_scale=1. / 16, sampling_ratio=0): + super(RoIAlign, self).__init__() + if isinstance(resolution, Integral): + resolution = [resolution, resolution] + self.pooled_height = resolution[0] + self.pooled_width = resolution[1] + self.spatial_scale = spatial_scale + self.sampling_ratio = sampling_ratio + + +@register +class RoIPool(object): + __op__ = fluid.layers.roi_pool + __append_doc__ = True + + def __init__(self, resolution=7, spatial_scale=1. / 16): + super(RoIPool, self).__init__() + if isinstance(resolution, Integral): + resolution = [resolution, resolution] + self.pooled_height = resolution[0] + self.pooled_width = resolution[1] + self.spatial_scale = spatial_scale + + +@register +class MultiBoxHead(object): + __op__ = fluid.layers.multi_box_head + __append_doc__ = True + + def __init__(self, + min_ratio=20, + max_ratio=90, + base_size=300, + min_sizes=[60.0, 105.0, 150.0, 195.0, 240.0, 285.0], + max_sizes=[[], 150.0, 195.0, 240.0, 285.0, 300.0], + aspect_ratios=[[2.], [2., 3.], [2., 3.], [2., 3.], [2., 3.], + [2., 3.]], + steps=None, + offset=0.5, + flip=True, + min_max_aspect_ratios_order=False, + kernel_size=1, + pad=0): + super(MultiBoxHead, self).__init__() + self.min_ratio = min_ratio + self.max_ratio = max_ratio + self.base_size = base_size + self.min_sizes = min_sizes + self.max_sizes = max_sizes + self.aspect_ratios = aspect_ratios + self.steps = steps + self.offset = offset + self.flip = flip + self.min_max_aspect_ratios_order = min_max_aspect_ratios_order + self.kernel_size = kernel_size + self.pad = pad + + +@register +@serializable +class SSDOutputDecoder(object): + __op__ = fluid.layers.detection_output + __append_doc__ = True + + def __init__(self, + nms_threshold=0.45, + nms_top_k=400, + keep_top_k=200, + score_threshold=0.01, + nms_eta=1.0, + background_label=0): + super(SSDOutputDecoder, self).__init__() + self.nms_threshold = nms_threshold + self.background_label = background_label + self.nms_top_k = nms_top_k + self.keep_top_k = keep_top_k + self.score_threshold = score_threshold + self.nms_eta = nms_eta + + +@register +@serializable +class RetinaTargetAssign(object): + __op__ = fluid.layers.retinanet_target_assign + __append_doc__ = True + + def __init__(self, positive_overlap=0.5, negative_overlap=0.4): + super(RetinaTargetAssign, self).__init__() + self.positive_overlap = positive_overlap + self.negative_overlap = negative_overlap + + +@register +@serializable +class RetinaOutputDecoder(object): + __op__ = fluid.layers.retinanet_detection_output + __append_doc__ = True + + def __init__(self, + score_thresh=0.05, + nms_thresh=0.3, + pre_nms_top_n=1000, + detections_per_im=100, + nms_eta=1.0): + super(RetinaOutputDecoder, self).__init__() + self.score_threshold = score_thresh + self.nms_threshold = nms_thresh + self.nms_top_k = pre_nms_top_n + self.keep_top_k = detections_per_im + self.nms_eta = nms_eta diff --git a/ppdet/modeling/roi_extractors/__init__.py b/ppdet/modeling/roi_extractors/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..15d2525db8c8dd2f72c09641ced94a0c0864b2a0 --- /dev/null +++ b/ppdet/modeling/roi_extractors/__init__.py @@ -0,0 +1,18 @@ +# 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 . import roi_extractor +from .roi_extractor import * diff --git a/ppdet/modeling/roi_extractors/roi_extractor.py b/ppdet/modeling/roi_extractors/roi_extractor.py new file mode 100644 index 0000000000000000000000000000000000000000..1caf3936f584bc0eb116d32a7e38559a917afe85 --- /dev/null +++ b/ppdet/modeling/roi_extractors/roi_extractor.py @@ -0,0 +1,97 @@ +# 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 paddle.fluid as fluid + +from ppdet.core.workspace import register +from ppdet.modeling.ops import RoIAlign, RoIPool + +__all__ = ['RoIPool', 'RoIAlign', 'FPNRoIAlign'] + + +@register +class FPNRoIAlign(object): + """ + RoI align pooling for FPN feature maps + Args: + sampling_ratio (int): number of sampling points + min_level (int): lowest level of FPN layer + max_level (int): highest level of FPN layer + canconical_level (int): the canconical FPN feature map level + canonical_size (int): the canconical FPN feature map size + box_resolution (int): box resolution + mask_resolution (int): mask roi resolution + """ + + def __init__(self, + sampling_ratio=0, + min_level=2, + max_level=5, + canconical_level=4, + canonical_size=224, + box_resolution=7, + mask_resolution=14): + super(FPNRoIAlign, self).__init__() + self.sampling_ratio = sampling_ratio + self.min_level = min_level + self.max_level = max_level + self.canconical_level = canconical_level + self.canonical_size = canonical_size + self.box_resolution = box_resolution + self.mask_resolution = mask_resolution + + def __call__(self, head_inputs, rois, spatial_scale, is_mask=False): + """ + Adopt RoI align onto several level of feature maps to get RoI features. + Distribute RoIs to different levels by area and get a list of RoI + features by distributed RoIs and their corresponding feature maps. + + Returns: + roi_feat(Variable): RoI features with shape of [M, C, R, R], + where M is the number of RoIs and R is RoI resolution + + """ + k_min = self.min_level + k_max = self.max_level + num_roi_lvls = k_max - k_min + 1 + name_list = list(head_inputs.keys()) + input_name_list = name_list[-num_roi_lvls:] + spatial_scale = spatial_scale[-num_roi_lvls:] + rois_dist, restore_index = fluid.layers.distribute_fpn_proposals( + rois, k_min, k_max, self.canconical_level, self.canonical_size) + # rois_dist is in ascend order + roi_out_list = [] + resolution = is_mask and self.mask_resolution or self.box_resolution + for lvl in range(num_roi_lvls): + name_index = num_roi_lvls - lvl - 1 + rois_input = rois_dist[lvl] + head_input = head_inputs[input_name_list[name_index]] + sc = spatial_scale[name_index] + roi_out = fluid.layers.roi_align( + input=head_input, + rois=rois_input, + pooled_height=resolution, + pooled_width=resolution, + spatial_scale=sc, + sampling_ratio=self.sampling_ratio) + roi_out_list.append(roi_out) + roi_feat_shuffle = fluid.layers.concat(roi_out_list) + roi_feat_ = fluid.layers.gather(roi_feat_shuffle, restore_index) + roi_feat = fluid.layers.lod_reset(roi_feat_, rois) + + return roi_feat diff --git a/ppdet/modeling/roi_heads/__init__.py b/ppdet/modeling/roi_heads/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..345a0eb3e30d69af15b8a5f0b4766e8693462e5d --- /dev/null +++ b/ppdet/modeling/roi_heads/__init__.py @@ -0,0 +1,23 @@ +# 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 . import bbox_head +from . import mask_head +from . import cascade_head + +from .bbox_head import * +from .mask_head import * +from .cascade_head import * diff --git a/ppdet/modeling/roi_heads/bbox_head.py b/ppdet/modeling/roi_heads/bbox_head.py new file mode 100644 index 0000000000000000000000000000000000000000..314aeb6087e05aa1c33d26b6b838075523ff2b10 --- /dev/null +++ b/ppdet/modeling/roi_heads/bbox_head.py @@ -0,0 +1,319 @@ +# 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 + +from collections import OrderedDict + +from paddle import fluid +from paddle.fluid.param_attr import ParamAttr +from paddle.fluid.initializer import Normal, Xavier +from paddle.fluid.regularizer import L2Decay +from paddle.fluid.initializer import MSRA + +from ppdet.modeling.ops import MultiClassNMS +from ppdet.modeling.ops import ConvNorm +from ppdet.core.workspace import register, serializable +from ppdet.experimental import mixed_precision_global_state + +__all__ = ['BBoxHead', 'TwoFCHead', 'XConvNormHead'] + + +@register +@serializable +class BoxCoder(object): + __op__ = fluid.layers.box_coder + __append_doc__ = True + + def __init__(self, + prior_box_var=[0.1, 0.1, 0.2, 0.2], + code_type='decode_center_size', + box_normalized=False, + axis=1): + super(BoxCoder, self).__init__() + self.prior_box_var = prior_box_var + self.code_type = code_type + self.box_normalized = box_normalized + self.axis = axis + + +@register +class XConvNormHead(object): + """ + RCNN head with serveral convolution layers + + Args: + conv_num (int): num of convolution layers for the rcnn head + conv_dim (int): num of filters for the conv layers + mlp_dim (int): num of filters for the fc layers + """ + __shared__ = ['norm_type', 'freeze_norm'] + + def __init__(self, + num_conv=4, + conv_dim=256, + mlp_dim=1024, + norm_type=None, + freeze_norm=False): + super(XConvNormHead, self).__init__() + self.conv_dim = conv_dim + self.mlp_dim = mlp_dim + self.num_conv = num_conv + self.norm_type = norm_type + self.freeze_norm = freeze_norm + + def __call__(self, roi_feat): + conv = roi_feat + fan = self.conv_dim * 3 * 3 + initializer = MSRA(uniform=False, fan_in=fan) + for i in range(self.num_conv): + name = 'bbox_head_conv' + str(i) + conv = ConvNorm( + conv, + self.conv_dim, + 3, + act='relu', + initializer=initializer, + norm_type=self.norm_type, + freeze_norm=self.freeze_norm, + name=name, + norm_name=name) + fan = conv.shape[1] * conv.shape[2] * conv.shape[3] + head_heat = fluid.layers.fc(input=conv, + size=self.mlp_dim, + act='relu', + name='fc6' + name, + param_attr=ParamAttr( + name='fc6%s_w' % name, + initializer=Xavier(fan_out=fan)), + bias_attr=ParamAttr( + name='fc6%s_b' % name, + learning_rate=2, + regularizer=L2Decay(0.))) + return head_heat + + +@register +class TwoFCHead(object): + """ + RCNN head with two Fully Connected layers + + Args: + mlp_dim (int): num of filters for the fc layers + """ + + def __init__(self, mlp_dim=1024): + super(TwoFCHead, self).__init__() + self.mlp_dim = mlp_dim + + def __call__(self, roi_feat): + fan = roi_feat.shape[1] * roi_feat.shape[2] * roi_feat.shape[3] + + mixed_precision_enabled = mixed_precision_global_state() is not None + + if mixed_precision_enabled: + roi_feat = fluid.layers.cast(roi_feat, 'float16') + + fc6 = fluid.layers.fc(input=roi_feat, + size=self.mlp_dim, + act='relu', + name='fc6', + param_attr=ParamAttr( + name='fc6_w', + initializer=Xavier(fan_out=fan)), + bias_attr=ParamAttr( + name='fc6_b', + learning_rate=2., + regularizer=L2Decay(0.))) + head_feat = fluid.layers.fc(input=fc6, + size=self.mlp_dim, + act='relu', + name='fc7', + param_attr=ParamAttr( + name='fc7_w', initializer=Xavier()), + bias_attr=ParamAttr( + name='fc7_b', + learning_rate=2., + regularizer=L2Decay(0.))) + + if mixed_precision_enabled: + head_feat = fluid.layers.cast(head_feat, 'float32') + + return head_feat + + +@register +class BBoxHead(object): + """ + RCNN bbox head + + Args: + head (object): the head module instance, e.g., `ResNetC5`, `TwoFCHead` + box_coder (object): `BoxCoder` instance + nms (object): `MultiClassNMS` instance + num_classes: number of output classes + """ + __inject__ = ['head', 'box_coder', 'nms'] + __shared__ = ['num_classes'] + + def __init__(self, + head, + box_coder=BoxCoder().__dict__, + nms=MultiClassNMS().__dict__, + num_classes=81): + super(BBoxHead, self).__init__() + self.head = head + self.num_classes = num_classes + self.box_coder = box_coder + self.nms = nms + if isinstance(box_coder, dict): + self.box_coder = BoxCoder(**box_coder) + if isinstance(nms, dict): + self.nms = MultiClassNMS(**nms) + self.head_feat = None + + def get_head_feat(self, input=None): + """ + Get the bbox head feature map. + """ + + if input is not None: + feat = self.head(input) + if isinstance(feat, OrderedDict): + feat = list(feat.values())[0] + self.head_feat = feat + return self.head_feat + + def _get_output(self, roi_feat): + """ + Get bbox head output. + + Args: + roi_feat (Variable): RoI feature from RoIExtractor. + + Returns: + cls_score(Variable): Output of rpn head with shape of + [N, num_anchors, H, W]. + bbox_pred(Variable): Output of rpn head with shape of + [N, num_anchors * 4, H, W]. + """ + head_feat = self.get_head_feat(roi_feat) + # when ResNetC5 output a single feature map + if not isinstance(self.head, TwoFCHead) and not isinstance( + self.head, XConvNormHead): + head_feat = fluid.layers.pool2d( + head_feat, pool_type='avg', global_pooling=True) + cls_score = fluid.layers.fc(input=head_feat, + size=self.num_classes, + act=None, + name='cls_score', + param_attr=ParamAttr( + name='cls_score_w', + initializer=Normal( + loc=0.0, scale=0.01)), + bias_attr=ParamAttr( + name='cls_score_b', + learning_rate=2., + regularizer=L2Decay(0.))) + bbox_pred = fluid.layers.fc(input=head_feat, + size=4 * self.num_classes, + act=None, + name='bbox_pred', + param_attr=ParamAttr( + name='bbox_pred_w', + initializer=Normal( + loc=0.0, scale=0.001)), + bias_attr=ParamAttr( + name='bbox_pred_b', + learning_rate=2., + regularizer=L2Decay(0.))) + return cls_score, bbox_pred + + def get_loss(self, roi_feat, labels_int32, bbox_targets, + bbox_inside_weights, bbox_outside_weights): + """ + Get bbox_head loss. + + Args: + roi_feat (Variable): RoI feature from RoIExtractor. + labels_int32(Variable): Class label of a RoI with shape [P, 1]. + P is the number of RoI. + bbox_targets(Variable): Box label of a RoI with shape + [P, 4 * class_nums]. + bbox_inside_weights(Variable): Indicates whether a box should + contribute to loss. Same shape as bbox_targets. + bbox_outside_weights(Variable): Indicates whether a box should + contribute to loss. Same shape as bbox_targets. + + Return: + Type: Dict + loss_cls(Variable): bbox_head loss. + loss_bbox(Variable): bbox_head loss. + """ + + cls_score, bbox_pred = self._get_output(roi_feat) + + labels_int64 = fluid.layers.cast(x=labels_int32, dtype='int64') + labels_int64.stop_gradient = True + loss_cls = fluid.layers.softmax_with_cross_entropy( + logits=cls_score, label=labels_int64, numeric_stable_mode=True) + loss_cls = fluid.layers.reduce_mean(loss_cls) + loss_bbox = fluid.layers.smooth_l1( + x=bbox_pred, + y=bbox_targets, + inside_weight=bbox_inside_weights, + outside_weight=bbox_outside_weights, + sigma=1.0) + loss_bbox = fluid.layers.reduce_mean(loss_bbox) + return {'loss_cls': loss_cls, 'loss_bbox': loss_bbox} + + def get_prediction(self, + roi_feat, + rois, + im_info, + im_shape, + return_box_score=False): + """ + Get prediction bounding box in test stage. + + Args: + roi_feat (Variable): RoI feature from RoIExtractor. + rois (Variable): Output of generate_proposals in rpn head. + im_info (Variable): A 2-D LoDTensor with shape [B, 3]. B is the + number of input images, each element consists of im_height, + im_width, im_scale. + im_shape (Variable): Actual shape of original image with shape + [B, 3]. B is the number of images, each element consists of + original_height, original_width, 1 + + Returns: + pred_result(Variable): Prediction result with shape [N, 6]. Each + row has 6 values: [label, confidence, xmin, ymin, xmax, ymax]. + N is the total number of prediction. + """ + cls_score, bbox_pred = self._get_output(roi_feat) + + im_scale = fluid.layers.slice(im_info, [1], starts=[2], ends=[3]) + im_scale = fluid.layers.sequence_expand(im_scale, rois) + boxes = rois / im_scale + cls_prob = fluid.layers.softmax(cls_score, use_cudnn=False) + bbox_pred = fluid.layers.reshape(bbox_pred, (-1, self.num_classes, 4)) + decoded_box = self.box_coder(prior_box=boxes, target_box=bbox_pred) + cliped_box = fluid.layers.box_clip(input=decoded_box, im_info=im_shape) + if return_box_score: + return {'bbox': cliped_box, 'score': cls_prob} + pred_result = self.nms(bboxes=cliped_box, scores=cls_prob) + return {'bbox': pred_result} diff --git a/ppdet/modeling/roi_heads/cascade_head.py b/ppdet/modeling/roi_heads/cascade_head.py new file mode 100644 index 0000000000000000000000000000000000000000..d36ff4c7541d8825e9491e696f787e10ef95b97e --- /dev/null +++ b/ppdet/modeling/roi_heads/cascade_head.py @@ -0,0 +1,321 @@ +# 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 paddle.fluid as fluid +from paddle.fluid.param_attr import ParamAttr +from paddle.fluid.initializer import Normal, Xavier +from paddle.fluid.regularizer import L2Decay +from paddle.fluid.initializer import MSRA + +from ppdet.modeling.ops import MultiClassNMS +from ppdet.modeling.ops import ConvNorm +from ppdet.core.workspace import register + +__all__ = ['CascadeBBoxHead'] + + +@register +class CascadeBBoxHead(object): + """ + Cascade RCNN bbox head + + Args: + head (object): the head module instance + nms (object): `MultiClassNMS` instance + num_classes: number of output classes + """ + __inject__ = ['head', 'nms'] + __shared__ = ['num_classes'] + + def __init__(self, head, nms=MultiClassNMS().__dict__, num_classes=81): + super(CascadeBBoxHead, self).__init__() + self.head = head + self.nms = nms + self.num_classes = num_classes + if isinstance(nms, dict): + self.nms = MultiClassNMS(**nms) + + def get_output(self, + roi_feat, + cls_agnostic_bbox_reg=2, + wb_scalar=1.0, + name=''): + """ + Get bbox head output. + + Args: + roi_feat (Variable): RoI feature from RoIExtractor. + cls_agnostic_bbox_reg(Int): BBox regressor are class agnostic. + wb_scalar(Float): Weights and Bias's learning rate. + name(String): Layer's name + + Returns: + cls_score(Variable): cls score. + bbox_pred(Variable): bbox regression. + """ + head_feat = self.head(roi_feat, wb_scalar, name) + cls_score = fluid.layers.fc(input=head_feat, + size=self.num_classes, + act=None, + name='cls_score' + name, + param_attr=ParamAttr( + name='cls_score%s_w' % name, + initializer=Normal( + loc=0.0, scale=0.01), + learning_rate=wb_scalar), + bias_attr=ParamAttr( + name='cls_score%s_b' % name, + learning_rate=wb_scalar * 2, + regularizer=L2Decay(0.))) + bbox_pred = fluid.layers.fc(input=head_feat, + size=4 * cls_agnostic_bbox_reg, + act=None, + name='bbox_pred' + name, + param_attr=ParamAttr( + name='bbox_pred%s_w' % name, + initializer=Normal( + loc=0.0, scale=0.001), + learning_rate=wb_scalar), + bias_attr=ParamAttr( + name='bbox_pred%s_b' % name, + learning_rate=wb_scalar * 2, + regularizer=L2Decay(0.))) + return cls_score, bbox_pred + + def get_loss(self, rcnn_pred_list, rcnn_target_list, rcnn_loss_weight_list): + """ + Get bbox_head loss. + + Args: + rcnn_pred_list(List): Cascade RCNN's head's output including + bbox_pred and cls_score + rcnn_target_list(List): Cascade rcnn's bbox and label target + rcnn_loss_weight_list(List): The weight of location and class loss + + Return: + loss_cls(Variable): bbox_head loss. + loss_bbox(Variable): bbox_head loss. + """ + loss_dict = {} + for i, (rcnn_pred, rcnn_target + ) in enumerate(zip(rcnn_pred_list, rcnn_target_list)): + labels_int64 = fluid.layers.cast(x=rcnn_target[1], dtype='int64') + labels_int64.stop_gradient = True + + loss_cls = fluid.layers.softmax_with_cross_entropy( + logits=rcnn_pred[0], + label=labels_int64, + numeric_stable_mode=True, ) + loss_cls = fluid.layers.reduce_mean( + loss_cls, name='loss_cls_' + str(i)) * rcnn_loss_weight_list[i] + + loss_bbox = fluid.layers.smooth_l1( + x=rcnn_pred[1], + y=rcnn_target[2], + inside_weight=rcnn_target[3], + outside_weight=rcnn_target[4], + sigma=1.0, # detectron use delta = 1./sigma**2 + ) + loss_bbox = fluid.layers.reduce_mean( + loss_bbox, + name='loss_bbox_' + str(i)) * rcnn_loss_weight_list[i] + + loss_dict['loss_cls_%d' % i] = loss_cls + loss_dict['loss_loc_%d' % i] = loss_bbox + + return loss_dict + + def get_prediction(self, + im_info, + im_shape, + roi_feat_list, + rcnn_pred_list, + proposal_list, + cascade_bbox_reg_weights, + cls_agnostic_bbox_reg=2, + return_box_score=False): + """ + Get prediction bounding box in test stage. + : + Args: + im_info (Variable): A 2-D LoDTensor with shape [B, 3]. B is the + number of input images, each element consists + of im_height, im_width, im_scale. + im_shape (Variable): Actual shape of original image with shape + [B, 3]. B is the number of images, each element consists of + original_height, original_width, 1 + rois_feat_list (List): RoI feature from RoIExtractor. + rcnn_pred_list (Variable): Cascade rcnn's head's output + including bbox_pred and cls_score + proposal_list (List): RPN proposal boxes. + cascade_bbox_reg_weights (List): BBox decode var. + cls_agnostic_bbox_reg(Int): BBox regressor are class agnostic + + Returns: + pred_result(Variable): Prediction result with shape [N, 6]. Each + row has 6 values: [label, confidence, xmin, ymin, xmax, ymax]. + N is the total number of prediction. + """ + self.im_scale = fluid.layers.slice(im_info, [1], starts=[2], ends=[3]) + boxes_cls_prob_l = [] + + rcnn_pred = rcnn_pred_list[-1] # stage 3 + repreat_num = 1 + repreat_num = 3 + bbox_reg_w = cascade_bbox_reg_weights[-1] + for i in range(repreat_num): + # cls score + if i < 2: + cls_score, _ = self.get_output( + roi_feat_list[-1], # roi_feat_3 + name='_' + str(i + 1) if i > 0 else '') + else: + cls_score = rcnn_pred[0] + cls_prob = fluid.layers.softmax(cls_score, use_cudnn=False) + boxes_cls_prob_l.append(cls_prob) + + boxes_cls_prob_mean = ( + boxes_cls_prob_l[0] + boxes_cls_prob_l[1] + boxes_cls_prob_l[2] + ) / 3.0 + + # bbox pred + proposals_boxes = proposal_list[-1] + im_scale_lod = fluid.layers.sequence_expand(self.im_scale, + proposals_boxes) + proposals_boxes = proposals_boxes / im_scale_lod + bbox_pred = rcnn_pred[1] + bbox_pred_new = fluid.layers.reshape(bbox_pred, + (-1, cls_agnostic_bbox_reg, 4)) + if cls_agnostic_bbox_reg == 2: + # only use fg box delta to decode box + bbox_pred_new = fluid.layers.slice( + bbox_pred_new, axes=[1], starts=[1], ends=[2]) + bbox_pred_new = fluid.layers.expand(bbox_pred_new, + [1, self.num_classes, 1]) + decoded_box = fluid.layers.box_coder( + prior_box=proposals_boxes, + prior_box_var=bbox_reg_w, + target_box=bbox_pred_new, + code_type='decode_center_size', + box_normalized=False, + axis=1) + + box_out = fluid.layers.box_clip(input=decoded_box, im_info=im_shape) + if return_box_score: + return {'bbox': box_out, 'score': boxes_cls_prob_mean} + pred_result = self.nms(bboxes=box_out, scores=boxes_cls_prob_mean) + return {"bbox": pred_result} + + +@register +class CascadeXConvNormHead(object): + """ + RCNN head with serveral convolution layers + + Args: + conv_num (int): num of convolution layers for the rcnn head + conv_dim (int): num of filters for the conv layers + mlp_dim (int): num of filters for the fc layers + """ + __shared__ = ['norm_type', 'freeze_norm'] + + def __init__(self, + num_conv=4, + conv_dim=256, + mlp_dim=1024, + norm_type=None, + freeze_norm=False): + super(CascadeXConvNormHead, self).__init__() + self.conv_dim = conv_dim + self.mlp_dim = mlp_dim + self.num_conv = num_conv + self.norm_type = norm_type + self.freeze_norm = freeze_norm + + def __call__(self, roi_feat, wb_scalar=1.0, name=''): + conv = roi_feat + fan = self.conv_dim * 3 * 3 + initializer = MSRA(uniform=False, fan_in=fan) + for i in range(self.num_conv): + name = 'bbox_head_conv' + str(i) + conv = ConvNorm( + conv, + self.conv_dim, + 3, + act='relu', + initializer=initializer, + norm_type=self.norm_type, + freeze_norm=self.freeze_norm, + lr_scale=wb_scalar, + name=name, + norm_name=name) + fan = conv.shape[1] * conv.shape[2] * conv.shape[3] + head_heat = fluid.layers.fc(input=conv, + size=self.mlp_dim, + act='relu', + name='fc6' + name, + param_attr=ParamAttr( + name='fc6%s_w' % name, + initializer=Xavier(fan_out=fan), + learning_rate=wb_scalar), + bias_attr=ParamAttr( + name='fc6%s_b' % name, + regularizer=L2Decay(0.), + learning_rate=wb_scalar * 2)) + return head_heat + + +@register +class CascadeTwoFCHead(object): + """ + RCNN head with serveral convolution layers + + Args: + mlp_dim (int): num of filters for the fc layers + """ + + def __init__(self, mlp_dim): + super(CascadeTwoFCHead, self).__init__() + self.mlp_dim = mlp_dim + + def __call__(self, roi_feat, wb_scalar=1.0, name=''): + fan = roi_feat.shape[1] * roi_feat.shape[2] * roi_feat.shape[3] + fc6 = fluid.layers.fc(input=roi_feat, + size=self.mlp_dim, + act='relu', + name='fc6' + name, + param_attr=ParamAttr( + name='fc6%s_w' % name, + initializer=Xavier(fan_out=fan), + learning_rate=wb_scalar), + bias_attr=ParamAttr( + name='fc6%s_b' % name, + learning_rate=wb_scalar * 2, + regularizer=L2Decay(0.))) + head_feat = fluid.layers.fc(input=fc6, + size=self.mlp_dim, + act='relu', + name='fc7' + name, + param_attr=ParamAttr( + name='fc7%s_w' % name, + initializer=Xavier(), + learning_rate=wb_scalar), + bias_attr=ParamAttr( + name='fc7%s_b' % name, + learning_rate=wb_scalar * 2, + regularizer=L2Decay(0.))) + return head_feat diff --git a/ppdet/modeling/roi_heads/mask_head.py b/ppdet/modeling/roi_heads/mask_head.py new file mode 100644 index 0000000000000000000000000000000000000000..f61add0402664113de83e5537acae459260481ee --- /dev/null +++ b/ppdet/modeling/roi_heads/mask_head.py @@ -0,0 +1,160 @@ +# 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 + +from paddle import fluid +from paddle.fluid.param_attr import ParamAttr +from paddle.fluid.initializer import MSRA +from paddle.fluid.regularizer import L2Decay + +from ppdet.core.workspace import register +from ppdet.modeling.ops import ConvNorm + +__all__ = ['MaskHead'] + + +@register +class MaskHead(object): + """ + RCNN mask head + Args: + num_convs (int): num of convolutions, 4 for FPN, 1 otherwise + conv_dim (int): num of channels after first convolution + resolution (int): size of the output mask + dilation (int): dilation rate + num_classes (int): number of output classes + """ + + __shared__ = ['num_classes'] + + def __init__(self, + num_convs=0, + conv_dim=256, + resolution=14, + dilation=1, + num_classes=81, + norm_type=None): + super(MaskHead, self).__init__() + self.num_convs = num_convs + self.conv_dim = conv_dim + self.resolution = resolution + self.dilation = dilation + self.num_classes = num_classes + self.norm_type = norm_type + + def _mask_conv_head(self, roi_feat, num_convs, norm_type): + if norm_type == 'gn': + for i in range(num_convs): + layer_name = "mask_inter_feat_" + str(i + 1) + fan = self.conv_dim * 3 * 3 + initializer = MSRA(uniform=False, fan_in=fan) + roi_feat = ConvNorm( + roi_feat, + self.conv_dim, + 3, + act='relu', + dilation=self.dilation, + initializer=initializer, + norm_type=self.norm_type, + name=layer_name, + norm_name=layer_name) + else: + for i in range(num_convs): + layer_name = "mask_inter_feat_" + str(i + 1) + fan = self.conv_dim * 3 * 3 + initializer = MSRA(uniform=False, fan_in=fan) + roi_feat = fluid.layers.conv2d( + input=roi_feat, + num_filters=self.conv_dim, + filter_size=3, + padding=1 * self.dilation, + act='relu', + stride=1, + dilation=self.dilation, + name=layer_name, + param_attr=ParamAttr( + name=layer_name + '_w', initializer=initializer), + bias_attr=ParamAttr( + name=layer_name + '_b', + learning_rate=2., + regularizer=L2Decay(0.))) + fan = roi_feat.shape[1] * 2 * 2 + feat = fluid.layers.conv2d_transpose( + input=roi_feat, + num_filters=self.conv_dim, + filter_size=2, + stride=2, + act='relu', + param_attr=ParamAttr( + name='conv5_mask_w', + initializer=MSRA( + uniform=False, fan_in=fan)), + bias_attr=ParamAttr( + name='conv5_mask_b', learning_rate=2., regularizer=L2Decay(0.))) + return feat + + def _get_output(self, roi_feat): + class_num = self.num_classes + # configure the conv number for FPN if necessary + head_feat = self._mask_conv_head(roi_feat, self.num_convs, + self.norm_type) + fan = class_num + mask_logits = fluid.layers.conv2d( + input=head_feat, + num_filters=class_num, + filter_size=1, + act=None, + param_attr=ParamAttr( + name='mask_fcn_logits_w', + initializer=MSRA( + uniform=False, fan_in=fan)), + bias_attr=ParamAttr( + name="mask_fcn_logits_b", + learning_rate=2., + regularizer=L2Decay(0.))) + return mask_logits + + def get_loss(self, roi_feat, mask_int32): + mask_logits = self._get_output(roi_feat) + num_classes = self.num_classes + resolution = self.resolution + dim = num_classes * resolution * resolution + mask_logits = fluid.layers.reshape(mask_logits, (-1, dim)) + + mask_label = fluid.layers.cast(x=mask_int32, dtype='float32') + mask_label.stop_gradient = True + loss_mask = fluid.layers.sigmoid_cross_entropy_with_logits( + x=mask_logits, label=mask_label, ignore_index=-1, normalize=True) + loss_mask = fluid.layers.reduce_sum(loss_mask, name='loss_mask') + return {'loss_mask': loss_mask} + + def get_prediction(self, roi_feat, bbox_pred): + """ + Get prediction mask in test stage. + + Args: + roi_feat (Variable): RoI feature from RoIExtractor. + bbox_pred (Variable): predicted bbox. + + Returns: + mask_pred (Variable): Prediction mask with shape + [N, num_classes, resolution, resolution]. + """ + mask_logits = self._get_output(roi_feat) + mask_prob = fluid.layers.sigmoid(mask_logits) + mask_prob = fluid.layers.lod_reset(mask_prob, bbox_pred) + return mask_prob diff --git a/ppdet/modeling/target_assigners.py b/ppdet/modeling/target_assigners.py new file mode 100644 index 0000000000000000000000000000000000000000..57d7bd738d2ee730effa3406df04fddedf1e5cd8 --- /dev/null +++ b/ppdet/modeling/target_assigners.py @@ -0,0 +1,73 @@ +# 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 + +from paddle import fluid + +from ppdet.core.workspace import register +from ppdet.modeling.ops import BBoxAssigner, MaskAssigner + +__all__ = ['BBoxAssigner', 'MaskAssigner', 'CascadeBBoxAssigner'] + + +@register +class CascadeBBoxAssigner(object): + __shared__ = ['num_classes'] + + def __init__(self, + batch_size_per_im=512, + fg_fraction=.25, + fg_thresh=[0.5, 0.6, 0.7], + bg_thresh_hi=[0.5, 0.6, 0.7], + bg_thresh_lo=[0., 0., 0.], + bbox_reg_weights=[10, 20, 30], + num_classes=81, + shuffle_before_sample=True): + super(CascadeBBoxAssigner, self).__init__() + self.batch_size_per_im = batch_size_per_im + self.fg_fraction = fg_fraction + self.fg_thresh = fg_thresh + self.bg_thresh_hi = bg_thresh_hi + self.bg_thresh_lo = bg_thresh_lo + self.bbox_reg_weights = bbox_reg_weights + self.class_nums = num_classes + self.use_random = shuffle_before_sample + + def __call__(self, input_rois, feed_vars, curr_stage): + + curr_bbox_reg_w = [ + 1. / self.bbox_reg_weights[curr_stage], + 1. / self.bbox_reg_weights[curr_stage], + 2. / self.bbox_reg_weights[curr_stage], + 2. / self.bbox_reg_weights[curr_stage], + ] + outs = fluid.layers.generate_proposal_labels( + rpn_rois=input_rois, + gt_classes=feed_vars['gt_label'], + is_crowd=feed_vars['is_crowd'], + gt_boxes=feed_vars['gt_box'], + im_info=feed_vars['im_info'], + batch_size_per_im=self.batch_size_per_im, + fg_thresh=self.fg_thresh[curr_stage], + bg_thresh_hi=self.bg_thresh_hi[curr_stage], + bg_thresh_lo=self.bg_thresh_lo[curr_stage], + bbox_reg_weights=curr_bbox_reg_w, + use_random=self.use_random, + class_nums=2, + is_cls_agnostic=True, + is_cascade_rcnn=True if curr_stage > 0 else False) + return outs diff --git a/ppdet/modeling/tests/__init__.py b/ppdet/modeling/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..33ed0ecf10ec4cad807ebb6df1590de65eeeab1e --- /dev/null +++ b/ppdet/modeling/tests/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/ppdet/modeling/tests/decorator_helper.py b/ppdet/modeling/tests/decorator_helper.py new file mode 100644 index 0000000000000000000000000000000000000000..894833ce15eab82ea06c2e66a8e53cb2e7e057b5 --- /dev/null +++ b/ppdet/modeling/tests/decorator_helper.py @@ -0,0 +1,33 @@ +# 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 paddle.fluid as fluid + +__all__ = ['prog_scope'] + + +def prog_scope(): + def __impl__(fn): + def __fn__(*args, **kwargs): + prog = fluid.Program() + startup_prog = fluid.Program() + scope = fluid.core.Scope() + with fluid.scope_guard(scope): + with fluid.program_guard(prog, startup_prog): + with fluid.unique_name.guard(): + fn(*args, **kwargs) + + return __fn__ + + return __impl__ diff --git a/ppdet/modeling/tests/test_architectures.py b/ppdet/modeling/tests/test_architectures.py new file mode 100644 index 0000000000000000000000000000000000000000..7df9580666858a834506a9a0beac742e548266f5 --- /dev/null +++ b/ppdet/modeling/tests/test_architectures.py @@ -0,0 +1,79 @@ +# 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 unittest +import numpy as np + +import paddle.fluid as fluid + +from ppdet.modeling.tests.decorator_helper import prog_scope +from ppdet.core.workspace import load_config, merge_config, create +from ppdet.modeling.model_input import create_feed + + +class TestFasterRCNN(unittest.TestCase): + def setUp(self): + self.set_config() + self.cfg = load_config(self.cfg_file) + self.detector_type = self.cfg['architecture'] + + def set_config(self): + self.cfg_file = 'configs/faster_rcnn_r50_1x.yml' + + @prog_scope() + def test_train(self): + train_feed = create(self.cfg['train_feed']) + model = create(self.detector_type) + _, feed_vars = create_feed(train_feed) + train_fetches = model.train(feed_vars) + + @prog_scope() + def test_test(self): + test_feed = create(self.cfg['eval_feed']) + model = create(self.detector_type) + _, feed_vars = create_feed(test_feed) + test_fetches = model.eval(feed_vars) + + +class TestMaskRCNN(TestFasterRCNN): + def set_config(self): + self.cfg_file = 'configs/mask_rcnn_r50_1x.yml' + + +class TestCascadeRCNN(TestFasterRCNN): + def set_config(self): + self.cfg_file = 'configs/cascade_rcnn_r50_fpn_1x.yml' + + +class TestYolov3(TestFasterRCNN): + def set_config(self): + self.cfg_file = 'configs/yolov3_darknet.yml' + + +class TestRetinaNet(TestFasterRCNN): + def set_config(self): + self.cfg_file = 'configs/retinanet_r50_fpn_1x.yml' + + +class TestSSD(TestFasterRCNN): + def set_config(self): + self.cfg_file = 'configs/ssd/ssd_mobilenet_v1_voc.yml' + + +if __name__ == '__main__': + unittest.main() diff --git a/ppdet/optimizer.py b/ppdet/optimizer.py new file mode 100644 index 0000000000000000000000000000000000000000..e695aad043b8778128f1b6870a27d2b0b3fe6adc --- /dev/null +++ b/ppdet/optimizer.py @@ -0,0 +1,141 @@ +# 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 + +from paddle import fluid + +import paddle.fluid.optimizer as optimizer +import paddle.fluid.regularizer as regularizer + +from ppdet.core.workspace import register, serializable + +__all__ = ['LearningRate', 'OptimizerBuilder'] + +logger = logging.getLogger(__name__) + + +@serializable +class PiecewiseDecay(object): + """ + Multi step learning rate decay + + Args: + gamma (float): decay factor + milestones (list): steps at which to decay learning rate + """ + + def __init__(self, gamma=0.1, milestones=[60000, 80000], values=None): + super(PiecewiseDecay, self).__init__() + self.gamma = gamma + self.milestones = milestones + self.values = values + + def __call__(self, base_lr=None, learning_rate=None): + if self.values is not None: + return fluid.layers.piecewise_decay(self.milestones, self.values) + assert base_lr is not None, "either base LR or values should be provided" + values = [base_lr] + lr = base_lr + for _ in self.milestones: + lr *= self.gamma + values.append(lr) + return fluid.layers.piecewise_decay(self.milestones, values) + + +@serializable +class LinearWarmup(object): + """ + Warm up learning rate linearly + + Args: + steps (int): warm up steps + start_factor (float): initial learning rate factor + """ + + def __init__(self, steps=500, start_factor=1. / 3): + super(LinearWarmup, self).__init__() + self.steps = steps + self.start_factor = start_factor + + def __call__(self, base_lr, learning_rate): + start_lr = base_lr * self.start_factor + + return fluid.layers.linear_lr_warmup( + learning_rate=learning_rate, + warmup_steps=self.steps, + start_lr=start_lr, + end_lr=base_lr) + + +@register +class LearningRate(object): + """ + Learning Rate configuration + + Args: + base_lr (float): base learning rate + schedulers (list): learning rate schedulers + """ + __category__ = 'optim' + + def __init__(self, + base_lr=0.01, + schedulers=[PiecewiseDecay(), LinearWarmup()]): + super(LearningRate, self).__init__() + self.base_lr = base_lr + self.schedulers = schedulers + + def __call__(self): + lr = None + for sched in self.schedulers: + lr = sched(self.base_lr, lr) + return lr + + +@register +class OptimizerBuilder(): + """ + Build optimizer handles + + Args: + regularizer (object): an `Regularizer` instance + optimizer (object): an `Optimizer` instance + """ + __category__ = 'optim' + + def __init__(self, + regularizer={'type': 'L2', + 'factor': .0001}, + optimizer={'type': 'Momentum', + 'momentum': .9}): + self.regularizer = regularizer + self.optimizer = optimizer + + def __call__(self, learning_rate): + reg_type = self.regularizer['type'] + 'Decay' + reg_factor = self.regularizer['factor'] + regularization = getattr(regularizer, reg_type)(reg_factor) + + optim_args = self.optimizer.copy() + optim_type = optim_args['type'] + del optim_args['type'] + op = getattr(optimizer, optim_type) + return op(learning_rate=learning_rate, + regularization=regularization, + **optim_args) diff --git a/ppdet/utils/__init__.py b/ppdet/utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d0c32e26092f6ea25771279418582a24ea449ab2 --- /dev/null +++ b/ppdet/utils/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/ppdet/utils/check.py b/ppdet/utils/check.py new file mode 100644 index 0000000000000000000000000000000000000000..9e816eaadcbf21b48e80ab6e607bff97269e7c39 --- /dev/null +++ b/ppdet/utils/check.py @@ -0,0 +1,47 @@ +# 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 +from __future__ import unicode_literals + +import sys + +import paddle.fluid as fluid + +import logging +logger = logging.getLogger(__name__) + +__all__ = ['check_gpu'] + + +def check_gpu(use_gpu): + """ + Log error and exit when set use_gpu=true in paddlepaddle + cpu version. + """ + err = "Config use_gpu cannot be set as true while you are " \ + "using paddlepaddle cpu version ! \nPlease try: \n" \ + "\t1. Install paddlepaddle-gpu to run model on GPU \n" \ + "\t2. Set use_gpu as false in config file to run " \ + "model on CPU" + + try: + if use_gpu and not fluid.is_compiled_with_cuda(): + logger.error(err) + sys.exit(1) + except Exception as e: + pass + diff --git a/ppdet/utils/checkpoint.py b/ppdet/utils/checkpoint.py new file mode 100644 index 0000000000000000000000000000000000000000..54c364812b041280148a1d0b85543209dc449724 --- /dev/null +++ b/ppdet/utils/checkpoint.py @@ -0,0 +1,291 @@ +# 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 +from __future__ import unicode_literals + +import errno +import os +import shutil +import time +import numpy as np +import re +import paddle.fluid as fluid + +from .download import get_weights_path + +import logging +logger = logging.getLogger(__name__) + +__all__ = [ + 'load_checkpoint', + 'load_and_fusebn', + 'load_params', + 'save', +] + + +def is_url(path): + """ + Whether path is URL. + Args: + path (string): URL string or not. + """ + return path.startswith('http://') or path.startswith('https://') + + +def _get_weight_path(path): + env = os.environ + if 'PADDLE_TRAINERS_NUM' in env and 'PADDLE_TRAINER_ID' in env: + trainer_id = int(env['PADDLE_TRAINER_ID']) + num_trainers = int(env['PADDLE_TRAINERS_NUM']) + if num_trainers <= 1: + path = get_weights_path(path) + else: + from ppdet.utils.download import map_path, WEIGHTS_HOME + weight_path = map_path(path, WEIGHTS_HOME) + lock_path = weight_path + '.lock' + if not os.path.exists(weight_path): + try: + os.makedirs(os.path.dirname(weight_path)) + except OSError as e: + if e.errno != errno.EEXIST: + raise + with open(lock_path, 'w'): # touch + os.utime(lock_path, None) + if trainer_id == 0: + get_weights_path(path) + os.remove(lock_path) + else: + while os.path.exists(lock_path): + time.sleep(1) + path = weight_path + else: + path = get_weights_path(path) + return path + + +def load_params(exe, prog, path, ignore_params=[]): + """ + Load model from the given path. + Args: + exe (fluid.Executor): The fluid.Executor object. + prog (fluid.Program): load weight to which Program object. + path (string): URL string or loca model path. + ignore_params (bool): ignore variable to load when finetuning. + It can be specified by finetune_exclude_pretrained_params + and the usage can refer to docs/TRANSFER_LEARNING.md + """ + + if is_url(path): + path = _get_weight_path(path) + + if not os.path.exists(path): + raise ValueError("Model pretrain path {} does not " + "exists.".format(path)) + + logger.info('Loading parameters from {}...'.format(path)) + + def _if_exist(var): + do_ignore = False + param_exist = os.path.exists(os.path.join(path, var.name)) + if len(ignore_params) > 0: + # Parameter related to num_classes will be ignored in finetuning + do_ignore_list = [ + bool(re.match(name, var.name)) for name in ignore_params + ] + do_ignore = any(do_ignore_list) + if do_ignore and param_exist: + logger.info('In load_params, ignore {}'.format(var.name)) + do_load = param_exist and not do_ignore + if do_load: + logger.debug('load weight {}'.format(var.name)) + return do_load + + fluid.io.load_vars(exe, path, prog, predicate=_if_exist) + + +def load_checkpoint(exe, prog, path): + """ + Load model from the given path. + Args: + exe (fluid.Executor): The fluid.Executor object. + prog (fluid.Program): load weight to which Program object. + path (string): URL string or loca model path. + """ + if is_url(path): + path = _get_weight_path(path) + + if not os.path.exists(path): + raise ValueError("Model checkpoint path {} does not " + "exists.".format(path)) + + logger.info('Loading checkpoint from {}...'.format(path)) + fluid.io.load_persistables(exe, path, prog) + + +def global_step(scope=None): + """ + Load global step in scope. + Args: + scope (fluid.Scope): load global step from which scope. If None, + from default global_scope(). + + Returns: + global step: int. + """ + if scope is None: + scope = fluid.global_scope() + v = scope.find_var('@LR_DECAY_COUNTER@') + step = np.array(v.get_tensor())[0] if v else 0 + return step + + +def save(exe, prog, path): + """ + Load model from the given path. + Args: + exe (fluid.Executor): The fluid.Executor object. + prog (fluid.Program): save weight from which Program object. + path (string): the path to save model. + """ + if os.path.isdir(path): + shutil.rmtree(path) + logger.info('Save model to {}.'.format(path)) + fluid.io.save_persistables(exe, path, prog) + + +def load_and_fusebn(exe, prog, path): + """ + Fuse params of batch norm to scale and bias. + + Args: + exe (fluid.Executor): The fluid.Executor object. + prog (fluid.Program): save weight from which Program object. + path (string): the path to save model. + """ + logger.info('Load model and fuse batch norm if have from {}...'.format( + path)) + + if is_url(path): + path = _get_weight_path(path) + + if not os.path.exists(path): + raise ValueError("Model path {} does not exists.".format(path)) + + def _if_exist(var): + b = os.path.exists(os.path.join(path, var.name)) + + if b: + logger.debug('load weight {}'.format(var.name)) + return b + + all_vars = list(filter(_if_exist, prog.list_vars())) + + # Since the program uses affine-channel, there is no running mean and var + # in the program, here append running mean and var. + # NOTE, the params of batch norm should be like: + # x_scale + # x_offset + # x_mean + # x_variance + # x is any prefix + mean_variances = set() + bn_vars = [] + + bn_in_path = True + + inner_prog = fluid.Program() + inner_start_prog = fluid.Program() + inner_block = inner_prog.global_block() + with fluid.program_guard(inner_prog, inner_start_prog): + for block in prog.blocks: + ops = list(block.ops) + if not bn_in_path: + break + for op in ops: + if op.type == 'affine_channel': + # remove 'scale' as prefix + scale_name = op.input('Scale')[0] # _scale + bias_name = op.input('Bias')[0] # _offset + prefix = scale_name[:-5] + mean_name = prefix + 'mean' + variance_name = prefix + 'variance' + + if not os.path.exists(os.path.join(path, mean_name)): + bn_in_path = False + break + if not os.path.exists(os.path.join(path, variance_name)): + bn_in_path = False + break + + bias = block.var(bias_name) + + mean_vb = inner_block.create_var( + name=mean_name, + type=bias.type, + shape=bias.shape, + dtype=bias.dtype, + persistable=True) + variance_vb = inner_block.create_var( + name=variance_name, + type=bias.type, + shape=bias.shape, + dtype=bias.dtype, + persistable=True) + + mean_variances.add(mean_vb) + mean_variances.add(variance_vb) + + bn_vars.append( + [scale_name, bias_name, mean_name, variance_name]) + + if not bn_in_path: + fluid.io.load_vars(exe, path, prog, vars=all_vars) + logger.warning( + "There is no paramters of batch norm in model {}. " + "Skip to fuse batch norm. And load paramters done.".format(path)) + return + + # load running mean and running variance on cpu place into global scope. + place = fluid.CPUPlace() + exe_cpu = fluid.Executor(place) + fluid.io.load_vars(exe_cpu, path, vars=[v for v in mean_variances]) + + # load params on real place into global scope. + fluid.io.load_vars(exe, path, prog, vars=all_vars) + + eps = 1e-5 + for names in bn_vars: + scale_name, bias_name, mean_name, var_name = names + + scale = fluid.global_scope().find_var(scale_name).get_tensor() + bias = fluid.global_scope().find_var(bias_name).get_tensor() + mean = fluid.global_scope().find_var(mean_name).get_tensor() + var = fluid.global_scope().find_var(var_name).get_tensor() + + scale_arr = np.array(scale) + bias_arr = np.array(bias) + mean_arr = np.array(mean) + var_arr = np.array(var) + + bn_std = np.sqrt(np.add(var_arr, eps)) + new_scale = np.float32(np.divide(scale_arr, bn_std)) + new_bias = bias_arr - mean_arr * new_scale + + # fuse to scale and bias in affine_channel + scale.set(new_scale, exe.place) + bias.set(new_bias, exe.place) diff --git a/ppdet/utils/cli.py b/ppdet/utils/cli.py new file mode 100644 index 0000000000000000000000000000000000000000..b8ba59d78f1ddf606012fd0cf6d71a71d79eea05 --- /dev/null +++ b/ppdet/utils/cli.py @@ -0,0 +1,151 @@ +# 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 argparse import ArgumentParser, RawDescriptionHelpFormatter + +import yaml +import re +from ppdet.core.workspace import get_registered_modules, dump_value + +__all__ = ['ColorTTY', 'ArgsParser'] + + +class ColorTTY(object): + def __init__(self): + super(ColorTTY, self).__init__() + self.colors = ['red', 'green', 'yellow', 'blue', 'magenta', 'cyan'] + + def __getattr__(self, attr): + if attr in self.colors: + color = self.colors.index(attr) + 31 + + def color_message(message): + return "[{}m{}".format(color, message) + + setattr(self, attr, color_message) + return color_message + + def bold(self, message): + return self.with_code('01', message) + + def with_code(self, code, message): + return "[{}m{}".format(code, message) + + +class ArgsParser(ArgumentParser): + def __init__(self): + super(ArgsParser, self).__init__( + formatter_class=RawDescriptionHelpFormatter) + self.add_argument("-c", "--config", help="configuration file to use") + self.add_argument( + "-o", "--opt", nargs='*', help="set configuration options") + + def parse_args(self, argv=None): + args = super(ArgsParser, self).parse_args(argv) + assert args.config is not None, \ + "Please specify --config=configure_file_path." + args.opt = self._parse_opt(args.opt) + return args + + def _parse_opt(self, opts): + config = {} + if not opts: + return config + for s in opts: + s = s.strip() + k, v = s.split('=', 1) + if '.' not in k: + config[k] = yaml.load(v, Loader=yaml.Loader) + else: + keys = k.split('.') + if keys[0] not in config: + config[keys[0]] = {} + cur = config[keys[0]] + for idx, key in enumerate(keys[1:]): + if idx == len(keys) - 2: + cur[key] = yaml.load(v, Loader=yaml.Loader) + else: + cur[key] = {} + cur = cur[key] + return config + + +def print_total_cfg(config): + modules = get_registered_modules() + color_tty = ColorTTY() + green = '___{}___'.format(color_tty.colors.index('green') + 31) + + styled = {} + for key in config.keys(): + if not config[key]: # empty schema + continue + + if key not in modules and not hasattr(config[key], '__dict__'): + styled[key] = config[key] + continue + elif key in modules: + module = modules[key] + else: + type_name = type(config[key]).__name__ + if type_name in modules: + module = modules[type_name].copy() + module.update({ + k: v + for k, v in config[key].__dict__.items() + if k in module.schema + }) + key += " ({})".format(type_name) + default = module.find_default_keys() + missing = module.find_missing_keys() + mismatch = module.find_mismatch_keys() + extra = module.find_extra_keys() + dep_missing = [] + for dep in module.inject: + if isinstance(module[dep], str) and module[dep] != '': + if module[dep] not in modules: # not a valid module + dep_missing.append(dep) + else: + dep_mod = modules[module[dep]] + # empty dict but mandatory + if not dep_mod and dep_mod.mandatory(): + dep_missing.append(dep) + override = list( + set(module.keys()) - set(default) - set(extra) - set(dep_missing)) + replacement = {} + for name in set(override + default + extra + mismatch + missing): + new_name = name + if name in missing: + value = "" + else: + value = module[name] + + if name in extra: + value = dump_value(value) + " " + elif name in mismatch: + value = dump_value(value) + " " + elif name in dep_missing: + value = dump_value(value) + " " + elif name in override and value != '': + mark = green + new_name = mark + name + replacement[new_name] = value + styled[key] = replacement + buffer = yaml.dump(styled, default_flow_style=False, default_style='') + buffer = (re.sub(r"", r"[31m[0m", buffer)) + buffer = (re.sub(r"", r"[33m[0m", buffer)) + buffer = (re.sub(r"", r"[31m[0m", buffer)) + buffer = (re.sub(r"", + r"[31m[0m", buffer)) + buffer = re.sub(r"___(\d+)___(.*?):", r"[\1m\2[0m:", buffer) + print(buffer) diff --git a/ppdet/utils/coco_eval.py b/ppdet/utils/coco_eval.py new file mode 100644 index 0000000000000000000000000000000000000000..cb5df475fb16f10fffa18a02920687780a3fe298 --- /dev/null +++ b/ppdet/utils/coco_eval.py @@ -0,0 +1,560 @@ +# 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 +from __future__ import unicode_literals + +import os +import sys +import json +import cv2 +import numpy as np +import matplotlib +matplotlib.use('Agg') + +import logging +logger = logging.getLogger(__name__) + +__all__ = [ + 'bbox_eval', + 'mask_eval', + 'bbox2out', + 'mask2out', + 'get_category_info', + 'proposal_eval', + 'cocoapi_eval', +] + + +def clip_bbox(bbox): + xmin = max(min(bbox[0], 1.), 0.) + ymin = max(min(bbox[1], 1.), 0.) + xmax = max(min(bbox[2], 1.), 0.) + ymax = max(min(bbox[3], 1.), 0.) + return xmin, ymin, xmax, ymax + + +def proposal_eval(results, anno_file, outfile, max_dets=(100, 300, 1000)): + assert 'proposal' in results[0] + assert outfile.endswith('.json') + + xywh_results = proposal2out(results) + assert len( + xywh_results) > 0, "The number of valid proposal detected is zero.\n \ + Please use reasonable model and check input data." + + with open(outfile, 'w') as f: + json.dump(xywh_results, f) + + cocoapi_eval(outfile, 'proposal', anno_file=anno_file, max_dets=max_dets) + # flush coco evaluation result + sys.stdout.flush() + + +def bbox_eval(results, + anno_file, + outfile, + with_background=True, + is_bbox_normalized=False): + assert 'bbox' in results[0] + assert outfile.endswith('.json') + from pycocotools.coco import COCO + + coco_gt = COCO(anno_file) + cat_ids = coco_gt.getCatIds() + + # when with_background = True, mapping category to classid, like: + # background:0, first_class:1, second_class:2, ... + clsid2catid = dict( + {i + int(with_background): catid + for i, catid in enumerate(cat_ids)}) + + xywh_results = bbox2out( + results, clsid2catid, is_bbox_normalized=is_bbox_normalized) + + if len(xywh_results) == 0: + logger.warning("The number of valid bbox detected is zero.\n \ + Please use reasonable model and check input data.\n \ + stop eval!") + return [0.0] + with open(outfile, 'w') as f: + json.dump(xywh_results, f) + + map_stats = cocoapi_eval(outfile, 'bbox', coco_gt=coco_gt) + # flush coco evaluation result + sys.stdout.flush() + return map_stats + + +def mask_eval(results, anno_file, outfile, resolution, thresh_binarize=0.5): + assert 'mask' in results[0] + assert outfile.endswith('.json') + from pycocotools.coco import COCO + + coco_gt = COCO(anno_file) + clsid2catid = {i + 1: v for i, v in enumerate(coco_gt.getCatIds())} + + segm_results = mask2out(results, clsid2catid, resolution, thresh_binarize) + if len(segm_results) == 0: + logger.warning("The number of valid mask detected is zero.\n \ + Please use reasonable model and check input data.") + return + + with open(outfile, 'w') as f: + json.dump(segm_results, f) + + cocoapi_eval(outfile, 'segm', coco_gt=coco_gt) + + +def cocoapi_eval(jsonfile, + style, + coco_gt=None, + anno_file=None, + max_dets=(100, 300, 1000)): + """ + Args: + jsonfile: Evaluation json file, eg: bbox.json, mask.json. + style: COCOeval style, can be `bbox` , `segm` and `proposal`. + coco_gt: Whether to load COCOAPI through anno_file, + eg: coco_gt = COCO(anno_file) + anno_file: COCO annotations file. + max_dets: COCO evaluation maxDets. + """ + assert coco_gt != None or anno_file != None + from pycocotools.coco import COCO + from pycocotools.cocoeval import COCOeval + + if coco_gt == None: + coco_gt = COCO(anno_file) + logger.info("Start evaluate...") + coco_dt = coco_gt.loadRes(jsonfile) + if style == 'proposal': + coco_eval = COCOeval(coco_gt, coco_dt, 'bbox') + coco_eval.params.useCats = 0 + coco_eval.params.maxDets = list(max_dets) + else: + coco_eval = COCOeval(coco_gt, coco_dt, style) + coco_eval.evaluate() + coco_eval.accumulate() + coco_eval.summarize() + return coco_eval.stats + + +def proposal2out(results, is_bbox_normalized=False): + xywh_res = [] + for t in results: + bboxes = t['proposal'][0] + lengths = t['proposal'][1][0] + im_ids = np.array(t['im_id'][0]) + if bboxes.shape == (1, 1) or bboxes is None: + continue + + k = 0 + for i in range(len(lengths)): + num = lengths[i] + im_id = int(im_ids[i][0]) + for j in range(num): + dt = bboxes[k] + xmin, ymin, xmax, ymax = dt.tolist() + + if is_bbox_normalized: + xmin, ymin, xmax, ymax = \ + clip_bbox([xmin, ymin, xmax, ymax]) + w = xmax - xmin + h = ymax - ymin + else: + w = xmax - xmin + 1 + h = ymax - ymin + 1 + + bbox = [xmin, ymin, w, h] + coco_res = { + 'image_id': im_id, + 'category_id': 1, + 'bbox': bbox, + 'score': 1.0 + } + xywh_res.append(coco_res) + k += 1 + return xywh_res + + +def bbox2out(results, clsid2catid, is_bbox_normalized=False): + """ + Args: + results: request a dict, should include: `bbox`, `im_id`, + if is_bbox_normalized=True, also need `im_shape`. + clsid2catid: class id to category id map of COCO2017 dataset. + is_bbox_normalized: whether or not bbox is normalized. + """ + xywh_res = [] + for t in results: + bboxes = t['bbox'][0] + lengths = t['bbox'][1][0] + im_ids = np.array(t['im_id'][0]) + if bboxes.shape == (1, 1) or bboxes is None: + continue + + k = 0 + for i in range(len(lengths)): + num = lengths[i] + im_id = int(im_ids[i][0]) + for j in range(num): + dt = bboxes[k] + clsid, score, xmin, ymin, xmax, ymax = dt.tolist() + catid = (clsid2catid[int(clsid)]) + + if is_bbox_normalized: + xmin, ymin, xmax, ymax = \ + clip_bbox([xmin, ymin, xmax, ymax]) + w = xmax - xmin + h = ymax - ymin + im_height, im_width = t['im_shape'][0][i].tolist() + xmin *= im_width + ymin *= im_height + w *= im_width + h *= im_height + else: + w = xmax - xmin + 1 + h = ymax - ymin + 1 + + bbox = [xmin, ymin, w, h] + coco_res = { + 'image_id': im_id, + 'category_id': catid, + 'bbox': bbox, + 'score': score + } + xywh_res.append(coco_res) + k += 1 + return xywh_res + + +def mask2out(results, clsid2catid, resolution, thresh_binarize=0.5): + import pycocotools.mask as mask_util + scale = (resolution + 2.0) / resolution + + segm_res = [] + + # for each batch + for t in results: + bboxes = t['bbox'][0] + + lengths = t['bbox'][1][0] + im_ids = np.array(t['im_id'][0]) + if bboxes.shape == (1, 1) or bboxes is None: + continue + if len(bboxes.tolist()) == 0: + continue + + masks = t['mask'][0] + + s = 0 + # for each sample + for i in range(len(lengths)): + num = lengths[i] + im_id = int(im_ids[i][0]) + im_shape = t['im_shape'][0][i] + + bbox = bboxes[s:s + num][:, 2:] + clsid_scores = bboxes[s:s + num][:, 0:2] + mask = masks[s:s + num] + s += num + + im_h = int(im_shape[0]) + im_w = int(im_shape[1]) + + expand_bbox = expand_boxes(bbox, scale) + expand_bbox = expand_bbox.astype(np.int32) + + padded_mask = np.zeros( + (resolution + 2, resolution + 2), dtype=np.float32) + + for j in range(num): + xmin, ymin, xmax, ymax = expand_bbox[j].tolist() + clsid, score = clsid_scores[j].tolist() + clsid = int(clsid) + padded_mask[1:-1, 1:-1] = mask[j, clsid, :, :] + + catid = clsid2catid[clsid] + + w = xmax - xmin + 1 + h = ymax - ymin + 1 + w = np.maximum(w, 1) + h = np.maximum(h, 1) + + resized_mask = cv2.resize(padded_mask, (w, h)) + resized_mask = np.array( + resized_mask > thresh_binarize, dtype=np.uint8) + im_mask = np.zeros((im_h, im_w), dtype=np.uint8) + + x0 = min(max(xmin, 0), im_w) + x1 = min(max(xmax + 1, 0), im_w) + y0 = min(max(ymin, 0), im_h) + y1 = min(max(ymax + 1, 0), im_h) + + im_mask[y0:y1, x0:x1] = resized_mask[(y0 - ymin):(y1 - ymin), ( + x0 - xmin):(x1 - xmin)] + segm = mask_util.encode( + np.array( + im_mask[:, :, np.newaxis], order='F'))[0] + catid = clsid2catid[clsid] + segm['counts'] = segm['counts'].decode('utf8') + coco_res = { + 'image_id': im_id, + 'category_id': catid, + 'segmentation': segm, + 'score': score + } + segm_res.append(coco_res) + return segm_res + + +def expand_boxes(boxes, scale): + """ + Expand an array of boxes by a given scale. + """ + w_half = (boxes[:, 2] - boxes[:, 0]) * .5 + h_half = (boxes[:, 3] - boxes[:, 1]) * .5 + x_c = (boxes[:, 2] + boxes[:, 0]) * .5 + y_c = (boxes[:, 3] + boxes[:, 1]) * .5 + + w_half *= scale + h_half *= scale + + boxes_exp = np.zeros(boxes.shape) + boxes_exp[:, 0] = x_c - w_half + boxes_exp[:, 2] = x_c + w_half + boxes_exp[:, 1] = y_c - h_half + boxes_exp[:, 3] = y_c + h_half + + return boxes_exp + + +def get_category_info(anno_file=None, + with_background=True, + use_default_label=False): + if use_default_label or anno_file is None \ + or not os.path.exists(anno_file): + logger.info("Not found annotation file {}, load " + "coco17 categories.".format(anno_file)) + return coco17_category_info(with_background) + else: + logger.info("Load categories from {}".format(anno_file)) + return get_category_info_from_anno(anno_file, with_background) + + +def get_category_info_from_anno(anno_file, with_background=True): + """ + Get class id to category id map and category id + to category name map from annotation file. + + Args: + anno_file (str): annotation file path + with_background (bool, default True): + whether load background as class 0. + """ + from pycocotools.coco import COCO + coco = COCO(anno_file) + cats = coco.loadCats(coco.getCatIds()) + clsid2catid = { + i + int(with_background): cat['id'] + for i, cat in enumerate(cats) + } + catid2name = {cat['id']: cat['name'] for cat in cats} + + return clsid2catid, catid2name + + +def coco17_category_info(with_background=True): + """ + Get class id to category id map and category id + to category name map of COCO2017 dataset + + Args: + with_background (bool, default True): + whether load background as class 0. + """ + clsid2catid = { + 1: 1, + 2: 2, + 3: 3, + 4: 4, + 5: 5, + 6: 6, + 7: 7, + 8: 8, + 9: 9, + 10: 10, + 11: 11, + 12: 13, + 13: 14, + 14: 15, + 15: 16, + 16: 17, + 17: 18, + 18: 19, + 19: 20, + 20: 21, + 21: 22, + 22: 23, + 23: 24, + 24: 25, + 25: 27, + 26: 28, + 27: 31, + 28: 32, + 29: 33, + 30: 34, + 31: 35, + 32: 36, + 33: 37, + 34: 38, + 35: 39, + 36: 40, + 37: 41, + 38: 42, + 39: 43, + 40: 44, + 41: 46, + 42: 47, + 43: 48, + 44: 49, + 45: 50, + 46: 51, + 47: 52, + 48: 53, + 49: 54, + 50: 55, + 51: 56, + 52: 57, + 53: 58, + 54: 59, + 55: 60, + 56: 61, + 57: 62, + 58: 63, + 59: 64, + 60: 65, + 61: 67, + 62: 70, + 63: 72, + 64: 73, + 65: 74, + 66: 75, + 67: 76, + 68: 77, + 69: 78, + 70: 79, + 71: 80, + 72: 81, + 73: 82, + 74: 84, + 75: 85, + 76: 86, + 77: 87, + 78: 88, + 79: 89, + 80: 90 + } + + catid2name = { + 0: 'background', + 1: 'person', + 2: 'bicycle', + 3: 'car', + 4: 'motorcycle', + 5: 'airplane', + 6: 'bus', + 7: 'train', + 8: 'truck', + 9: 'boat', + 10: 'traffic light', + 11: 'fire hydrant', + 13: 'stop sign', + 14: 'parking meter', + 15: 'bench', + 16: 'bird', + 17: 'cat', + 18: 'dog', + 19: 'horse', + 20: 'sheep', + 21: 'cow', + 22: 'elephant', + 23: 'bear', + 24: 'zebra', + 25: 'giraffe', + 27: 'backpack', + 28: 'umbrella', + 31: 'handbag', + 32: 'tie', + 33: 'suitcase', + 34: 'frisbee', + 35: 'skis', + 36: 'snowboard', + 37: 'sports ball', + 38: 'kite', + 39: 'baseball bat', + 40: 'baseball glove', + 41: 'skateboard', + 42: 'surfboard', + 43: 'tennis racket', + 44: 'bottle', + 46: 'wine glass', + 47: 'cup', + 48: 'fork', + 49: 'knife', + 50: 'spoon', + 51: 'bowl', + 52: 'banana', + 53: 'apple', + 54: 'sandwich', + 55: 'orange', + 56: 'broccoli', + 57: 'carrot', + 58: 'hot dog', + 59: 'pizza', + 60: 'donut', + 61: 'cake', + 62: 'chair', + 63: 'couch', + 64: 'potted plant', + 65: 'bed', + 67: 'dining table', + 70: 'toilet', + 72: 'tv', + 73: 'laptop', + 74: 'mouse', + 75: 'remote', + 76: 'keyboard', + 77: 'cell phone', + 78: 'microwave', + 79: 'oven', + 80: 'toaster', + 81: 'sink', + 82: 'refrigerator', + 84: 'book', + 85: 'clock', + 86: 'vase', + 87: 'scissors', + 88: 'teddy bear', + 89: 'hair drier', + 90: 'toothbrush' + } + + if not with_background: + clsid2catid = {k - 1: v for k, v in clsid2catid.items()} + + return clsid2catid, catid2name diff --git a/ppdet/utils/colormap.py b/ppdet/utils/colormap.py new file mode 100644 index 0000000000000000000000000000000000000000..566185ef90390e2f45747bee48e3b008f7dfc0e9 --- /dev/null +++ b/ppdet/utils/colormap.py @@ -0,0 +1,56 @@ +# 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 +from __future__ import unicode_literals + +import numpy as np + + +def colormap(rgb=False): + """ + Get colormap + """ + color_list = np.array([ + 0.000, 0.447, 0.741, 0.850, 0.325, 0.098, 0.929, 0.694, 0.125, 0.494, + 0.184, 0.556, 0.466, 0.674, 0.188, 0.301, 0.745, 0.933, 0.635, 0.078, + 0.184, 0.300, 0.300, 0.300, 0.600, 0.600, 0.600, 1.000, 0.000, 0.000, + 1.000, 0.500, 0.000, 0.749, 0.749, 0.000, 0.000, 1.000, 0.000, 0.000, + 0.000, 1.000, 0.667, 0.000, 1.000, 0.333, 0.333, 0.000, 0.333, 0.667, + 0.000, 0.333, 1.000, 0.000, 0.667, 0.333, 0.000, 0.667, 0.667, 0.000, + 0.667, 1.000, 0.000, 1.000, 0.333, 0.000, 1.000, 0.667, 0.000, 1.000, + 1.000, 0.000, 0.000, 0.333, 0.500, 0.000, 0.667, 0.500, 0.000, 1.000, + 0.500, 0.333, 0.000, 0.500, 0.333, 0.333, 0.500, 0.333, 0.667, 0.500, + 0.333, 1.000, 0.500, 0.667, 0.000, 0.500, 0.667, 0.333, 0.500, 0.667, + 0.667, 0.500, 0.667, 1.000, 0.500, 1.000, 0.000, 0.500, 1.000, 0.333, + 0.500, 1.000, 0.667, 0.500, 1.000, 1.000, 0.500, 0.000, 0.333, 1.000, + 0.000, 0.667, 1.000, 0.000, 1.000, 1.000, 0.333, 0.000, 1.000, 0.333, + 0.333, 1.000, 0.333, 0.667, 1.000, 0.333, 1.000, 1.000, 0.667, 0.000, + 1.000, 0.667, 0.333, 1.000, 0.667, 0.667, 1.000, 0.667, 1.000, 1.000, + 1.000, 0.000, 1.000, 1.000, 0.333, 1.000, 1.000, 0.667, 1.000, 0.167, + 0.000, 0.000, 0.333, 0.000, 0.000, 0.500, 0.000, 0.000, 0.667, 0.000, + 0.000, 0.833, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 0.167, 0.000, + 0.000, 0.333, 0.000, 0.000, 0.500, 0.000, 0.000, 0.667, 0.000, 0.000, + 0.833, 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 0.167, 0.000, 0.000, + 0.333, 0.000, 0.000, 0.500, 0.000, 0.000, 0.667, 0.000, 0.000, 0.833, + 0.000, 0.000, 1.000, 0.000, 0.000, 0.000, 0.143, 0.143, 0.143, 0.286, + 0.286, 0.286, 0.429, 0.429, 0.429, 0.571, 0.571, 0.571, 0.714, 0.714, + 0.714, 0.857, 0.857, 0.857, 1.000, 1.000, 1.000 + ]).astype(np.float32) + color_list = color_list.reshape((-1, 3)) * 255 + if not rgb: + color_list = color_list[:, ::-1] + return color_list diff --git a/ppdet/utils/dist_utils.py b/ppdet/utils/dist_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..32eead4a797ba70cb6980e0368ff9873102680c2 --- /dev/null +++ b/ppdet/utils/dist_utils.py @@ -0,0 +1,41 @@ +# 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 + +import os + +import paddle.fluid as fluid + + +def nccl2_prepare(trainer_id, startup_prog, main_prog): + config = fluid.DistributeTranspilerConfig() + config.mode = "nccl2" + t = fluid.DistributeTranspiler(config=config) + t.transpile( + trainer_id, + trainers=os.environ.get('PADDLE_TRAINER_ENDPOINTS'), + current_endpoint=os.environ.get('PADDLE_CURRENT_ENDPOINT'), + startup_program=startup_prog, + program=main_prog) + + +def prepare_for_multi_process(exe, build_strategy, startup_prog, main_prog): + trainer_id = int(os.environ.get('PADDLE_TRAINER_ID', 0)) + num_trainers = int(os.environ.get('PADDLE_TRAINERS_NUM', 1)) + if num_trainers < 2: + return + build_strategy.num_trainers = num_trainers + build_strategy.trainer_id = trainer_id + nccl2_prepare(trainer_id, startup_prog, main_prog) diff --git a/ppdet/utils/download.py b/ppdet/utils/download.py new file mode 100644 index 0000000000000000000000000000000000000000..6de9138ec40e1ef29a473ef8354f778ec9260ef5 --- /dev/null +++ b/ppdet/utils/download.py @@ -0,0 +1,357 @@ +# 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 os.path as osp +import shutil +import requests +import tqdm +import hashlib +import tarfile +import zipfile + +from .voc_utils import create_list + +import logging +logger = logging.getLogger(__name__) + +__all__ = ['get_weights_path', 'get_dataset_path'] + +WEIGHTS_HOME = osp.expanduser("~/.cache/paddle/weights") +DATASET_HOME = osp.expanduser("~/.cache/paddle/dataset") + +# dict of {dataset_name: (download_info, sub_dirs)} +# download info: (url, md5sum) +DATASETS = { + 'coco': ([ + ( + 'http://images.cocodataset.org/zips/train2017.zip', + 'cced6f7f71b7629ddf16f17bbcfab6b2', ), + ( + 'http://images.cocodataset.org/zips/val2017.zip', + '442b8da7639aecaf257c1dceb8ba8c80', ), + ( + 'http://images.cocodataset.org/annotations/annotations_trainval2017.zip', + 'f4bbac642086de4f52a3fdda2de5fa2c', ), + ], ["annotations", "train2017", "val2017"]), + 'voc': ([ + ( + 'http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar', + '6cd6e144f989b92b3379bac3b3de84fd', ), + ( + 'http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtrainval_06-Nov-2007.tar', + 'c52e279531787c972589f7e41ab4ae64', ), + ( + 'http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtest_06-Nov-2007.tar', + 'b6e924de25625d8de591ea690078ad9f', ), + ], ["VOCdevkit/VOC2012", "VOCdevkit/VOC2007"]), + 'wider_face': ([ + ( + 'https://dataset.bj.bcebos.com/wider_face/WIDER_train.zip', + '3fedf70df600953d25982bcd13d91ba2', ), + ( + 'https://dataset.bj.bcebos.com/wider_face/WIDER_val.zip', + 'dfa7d7e790efa35df3788964cf0bbaea', ), + ( + 'https://dataset.bj.bcebos.com/wider_face/wider_face_split.zip', + 'a4a898d6193db4b9ef3260a68bad0dc7', ), + ], ["WIDER_train", "WIDER_val", "wider_face_split"]), + 'fruit': ([ + ( + 'https://dataset.bj.bcebos.com/PaddleDetection_demo/fruit-detection.tar', + '374554a7633b1b68d6a5fbb7c061b8ba', ), + ], ["fruit-detection"]), +} + +DOWNLOAD_RETRY_LIMIT = 3 + + +def get_weights_path(url): + """Get weights path from WEIGHT_HOME, if not exists, + download it from url. + """ + path, _ = get_path(url, WEIGHTS_HOME) + return path + + +def get_dataset_path(path, annotation, image_dir): + """ + If path exists, return path. + Otherwise, get dataset path from DATASET_HOME, if not exists, + download it. + """ + if _dataset_exists(path, annotation, image_dir): + return path + + logger.info("Dataset {} is not valid for reason above, try searching {} or " + "downloading dataset...".format( + osp.realpath(path), DATASET_HOME)) + + for name, dataset in DATASETS.items(): + if os.path.split(path.strip().lower())[-1] == name: + logger.info("Parse dataset_dir {} as dataset " + "{}".format(path, name)) + data_dir = osp.join(DATASET_HOME, name) + + # For voc, only check dir VOCdevkit/VOC2012, VOCdevkit/VOC2007 + if name == 'voc': + exists = True + for sub_dir in dataset[1]: + check_dir = osp.join(data_dir, sub_dir) + if osp.exists(check_dir): + logger.info("Found {}".format(check_dir)) + else: + exists = False + if exists: + return data_dir + + # voc exist is checked above, voc is not exist here + check_exist = name != 'voc' + for url, md5sum in dataset[0]: + get_path(url, data_dir, md5sum, check_exist) + + # voc should create list after download + if name == 'voc': + create_voc_list(data_dir) + return data_dir + + # not match any dataset in DATASETS + raise ValueError("Dataset {} is not valid and cannot parse dataset type " + "'{}' for automaticly downloading, which only supports " + "'voc' and 'coco' currently".format(path, + osp.split(path)[-1])) + + +def create_voc_list(data_dir, devkit_subdir='VOCdevkit'): + logger.info("Create voc file list...") + devkit_dir = osp.join(data_dir, devkit_subdir) + years = ['2007', '2012'] + + # 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 + create_list(devkit_dir, years, data_dir) + logger.info("Create voc file list finished") + + +def map_path(url, root_dir): + # parse path after download to decompress under root_dir + fname = url.split('/')[-1] + zip_formats = ['.zip', '.tar', '.gz'] + fpath = fname + for zip_format in zip_formats: + fpath = fpath.replace(zip_format, '') + return osp.join(root_dir, fpath) + + +def get_path(url, root_dir, md5sum=None, check_exist=True): + """ Download from given url to root_dir. + if file or directory specified by url is exists under + root_dir, return the path directly, otherwise download + from url and decompress it, return the path. + + url (str): download url + root_dir (str): root dir for downloading, it should be + WEIGHTS_HOME or DATASET_HOME + md5sum (str): md5 sum of download package + """ + # parse path after download to decompress under root_dir + fullpath = map_path(url, root_dir) + + # For same zip file, decompressed directory name different + # from zip file name, rename by following map + decompress_name_map = { + "VOCtrainval_11-May-2012": "VOCdevkit/VOC2012", + "VOCtrainval_06-Nov-2007": "VOCdevkit/VOC2007", + "VOCtest_06-Nov-2007": "VOCdevkit/VOC2007", + "annotations_trainval": "annotations" + } + for k, v in decompress_name_map.items(): + if fullpath.find(k) >= 0: + fullpath = '/'.join(fullpath.split('/')[:-1] + [v]) + + exist_flag = False + if osp.exists(fullpath) and check_exist: + exist_flag = True + logger.info("Found {}".format(fullpath)) + else: + exist_flag = False + fullname = _download(url, root_dir, md5sum) + _decompress(fullname) + + return fullpath, exist_flag + + +def download_dataset(path, dataset=None): + if dataset not in DATASETS.keys(): + logger.error("Unknown dataset {}, it should be " + "{}".format(dataset, DATASETS.keys())) + return + dataset_info = DATASETS[dataset][0] + for info in dataset_info: + get_path(info[0], path, info[1], False) + logger.info("Download dataset {} finished.".format(dataset)) + + +def _dataset_exists(path, annotation, image_dir): + """ + Check if user define dataset exists + """ + if not osp.exists(path): + logger.info("Config dataset_dir {} is not exits, " + "dataset config is not valid".format(path)) + return False + + if annotation: + annotation_path = osp.join(path, annotation) + if not osp.isfile(annotation_path): + logger.info("Config annotation {} is not a " + "file, dataset config is not " + "valid".format(annotation_path)) + return False + if image_dir: + image_path = osp.join(path, image_dir) + if not osp.isdir(image_path): + logger.info("Config image_dir {} is not a " + "directory, dataset config is not " + "valid".format(image_path)) + return False + return True + + +def _download(url, path, md5sum=None): + """ + Download from url, save to path. + + url (str): download url + path (str): download to given path + """ + if not osp.exists(path): + os.makedirs(path) + + fname = url.split('/')[-1] + fullname = osp.join(path, fname) + retry_cnt = 0 + + while not (osp.exists(fullname) and _md5check(fullname, md5sum)): + if retry_cnt < DOWNLOAD_RETRY_LIMIT: + retry_cnt += 1 + else: + raise RuntimeError("Download from {} failed. " + "Retry limit reached".format(url)) + + logger.info("Downloading {} from {}".format(fname, url)) + + req = requests.get(url, stream=True) + if req.status_code != 200: + raise RuntimeError("Downloading from {} failed with code " + "{}!".format(url, req.status_code)) + + # For protecting download interupted, download to + # tmp_fullname firstly, move tmp_fullname to fullname + # after download finished + tmp_fullname = fullname + "_tmp" + total_size = req.headers.get('content-length') + with open(tmp_fullname, 'wb') as f: + if total_size: + for chunk in tqdm.tqdm( + req.iter_content(chunk_size=1024), + total=(int(total_size) + 1023) // 1024, + unit='KB'): + f.write(chunk) + else: + for chunk in req.iter_content(chunk_size=1024): + if chunk: + f.write(chunk) + shutil.move(tmp_fullname, fullname) + + return fullname + + +def _md5check(fullname, md5sum=None): + if md5sum is None: + return True + + logger.info("File {} md5 checking...".format(fullname)) + md5 = hashlib.md5() + with open(fullname, 'rb') as f: + for chunk in iter(lambda: f.read(4096), b""): + md5.update(chunk) + calc_md5sum = md5.hexdigest() + + if calc_md5sum != md5sum: + logger.info("File {} md5 check failed, {}(calc) != " + "{}(base)".format(fullname, calc_md5sum, md5sum)) + return False + return True + + +def _decompress(fname): + """ + Decompress for zip and tar file + """ + logger.info("Decompressing {}...".format(fname)) + + # For protecting decompressing interupted, + # decompress to fpath_tmp directory firstly, if decompress + # successed, move decompress files to fpath and delete + # fpath_tmp and remove download compress file. + fpath = '/'.join(fname.split('/')[:-1]) + fpath_tmp = osp.join(fpath, 'tmp') + if osp.isdir(fpath_tmp): + shutil.rmtree(fpath_tmp) + os.makedirs(fpath_tmp) + + if fname.find('tar') >= 0: + with tarfile.open(fname) as tf: + tf.extractall(path=fpath_tmp) + elif fname.find('zip') >= 0: + with zipfile.ZipFile(fname) as zf: + zf.extractall(path=fpath_tmp) + else: + raise TypeError("Unsupport compress file type {}".format(fname)) + + for f in os.listdir(fpath_tmp): + src_dir = osp.join(fpath_tmp, f) + dst_dir = osp.join(fpath, f) + _move_and_merge_tree(src_dir, dst_dir) + + shutil.rmtree(fpath_tmp) + os.remove(fname) + + +def _move_and_merge_tree(src, dst): + """ + Move src directory to dst, if dst is already exists, + merge src to dst + """ + if not osp.exists(dst): + shutil.move(src, dst) + else: + for fp in os.listdir(src): + src_fp = osp.join(src, fp) + dst_fp = osp.join(dst, fp) + if osp.isdir(src_fp): + if osp.isdir(dst_fp): + _move_and_merge_tree(src_fp, dst_fp) + else: + shutil.move(src_fp, dst_fp) + elif osp.isfile(src_fp) and \ + not osp.isfile(dst_fp): + shutil.move(src_fp, dst_fp) diff --git a/ppdet/utils/eval_utils.py b/ppdet/utils/eval_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..dbded30197b46e12282d50991406a6585c0cb572 --- /dev/null +++ b/ppdet/utils/eval_utils.py @@ -0,0 +1,247 @@ +# 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 numpy as np +import os +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 + +__all__ = ['parse_fetches', 'eval_run', 'eval_results', 'json_eval_results'] + +logger = logging.getLogger(__name__) + + +def parse_fetches(fetches, prog=None, extra_keys=None): + """ + Parse fetch variable infos from model fetches, + values for fetch_list and keys for stat + """ + keys, values = [], [] + cls = [] + for k, v in fetches.items(): + if hasattr(v, 'name'): + keys.append(k) + v.persistable = True + values.append(v.name) + else: + cls.append(v) + + if prog is not None and extra_keys is not None: + for k in extra_keys: + try: + v = fluid.framework._get_var(k, prog) + keys.append(k) + values.append(v.name) + except Exception: + pass + + return keys, values, cls + + +def length2lod(length_lod): + offset_lod = [0] + for i in length_lod: + offset_lod.append(offset_lod[-1] + i) + return [offset_lod] + + +def get_sub_feed(input, place): + new_dict = {} + res_feed = {} + key_name = ['bbox', 'im_info', 'im_id', 'im_shape', 'bbox_flip'] + for k in key_name: + if k in input.keys(): + new_dict[k] = input[k] + for k in input.keys(): + if 'image' in k: + new_dict[k] = input[k] + for k, v in new_dict.items(): + data_t = fluid.LoDTensor() + data_t.set(v[0], place) + if 'bbox' in k: + lod = length2lod(v[1][0]) + data_t.set_lod(lod) + res_feed[k] = data_t + return res_feed + + +def clean_res(result, keep_name_list): + clean_result = {} + for k in result.keys(): + if k in keep_name_list: + clean_result[k] = result[k] + result.clear() + return clean_result + + +def eval_run(exe, + compile_program, + pyreader, + keys, + values, + cls, + cfg=None, + sub_prog=None, + sub_keys=None, + sub_values=None): + """ + Run evaluation program, return program outputs. + """ + iter_id = 0 + results = [] + if len(cls) != 0: + values = [] + for i in range(len(cls)): + _, accum_map = cls[i].get_map_var() + cls[i].reset(exe) + values.append(accum_map) + + images_num = 0 + start_time = time.time() + has_bbox = 'bbox' in keys + + try: + pyreader.start() + while True: + outs = exe.run(compile_program, + fetch_list=values, + return_numpy=False) + res = { + k: (np.array(v), v.recursive_sequence_lengths()) + for k, v in zip(keys, outs) + } + multi_scale_test = getattr(cfg, 'MultiScaleTEST', None) + mask_multi_scale_test = multi_scale_test and 'Mask' in cfg.architecture + + if multi_scale_test: + post_res = mstest_box_post_process(res, cfg) + res.update(post_res) + if mask_multi_scale_test: + place = fluid.CUDAPlace(0) if cfg.use_gpu else fluid.CPUPlace() + sub_feed = get_sub_feed(res, place) + sub_prog_outs = exe.run(sub_prog, + feed=sub_feed, + fetch_list=sub_values, + return_numpy=False) + sub_prog_res = { + k: (np.array(v), v.recursive_sequence_lengths()) + for k, v in zip(sub_keys, sub_prog_outs) + } + post_res = mstest_mask_post_process(sub_prog_res, cfg) + res.update(post_res) + if multi_scale_test: + res = clean_res( + res, ['im_info', 'bbox', 'im_id', 'im_shape', 'mask']) + results.append(res) + if iter_id % 100 == 0: + logger.info('Test iter {}'.format(iter_id)) + iter_id += 1 + images_num += len(res['bbox'][1][0]) if has_bbox else 1 + except (StopIteration, fluid.core.EOFException): + pyreader.reset() + logger.info('Test finish iter {}'.format(iter_id)) + + end_time = time.time() + fps = images_num / (end_time - start_time) + if has_bbox: + logger.info('Total number of images: {}, inference time: {} fps.'. + format(images_num, fps)) + else: + logger.info('Total iteration: {}, inference time: {} batch/s.'.format( + images_num, fps)) + + return results + + +def eval_results(results, + feed, + metric, + num_classes, + resolution=None, + is_bbox_normalized=False, + output_directory=None, + map_type='11point'): + """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) + if 'proposal' in results[0]: + output = 'proposal.json' + if output_directory: + output = os.path.join(output_directory, 'proposal.json') + proposal_eval(results, anno_file, output) + if 'bbox' in results[0]: + output = 'bbox.json' + if output_directory: + output = os.path.join(output_directory, 'bbox.json') + + box_ap_stats = bbox_eval( + results, + anno_file, + output, + with_background, + is_bbox_normalized=is_bbox_normalized) + + if 'mask' in results[0]: + output = 'mask.json' + if output_directory: + output = os.path.join(output_directory, 'mask.json') + mask_eval(results, anno_file, output, resolution) + else: + if 'accum_map' in results[-1]: + res = np.mean(results[-1]['accum_map'][0]) + logger.info('mAP: {:.2f}'.format(res * 100.)) + box_ap_stats.append(res * 100.) + elif 'bbox' in results[0]: + box_ap = voc_bbox_eval( + results, + num_classes, + is_bbox_normalized=is_bbox_normalized, + map_type=map_type) + box_ap_stats.append(box_ap) + return box_ap_stats + + +def json_eval_results(feed, metric, json_directory=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) + json_file_list = ['proposal.json', 'bbox.json', 'mask.json'] + if json_directory: + assert os.path.exists( + json_directory), "The json directory:{} does not exist".format( + json_directory) + for k, v in enumerate(json_file_list): + json_file_list[k] = os.path.join(str(json_directory), v) + + coco_eval_style = ['proposal', 'bbox', 'segm'] + for i, v_json in enumerate(json_file_list): + if os.path.exists(v_json): + cocoapi_eval(v_json, coco_eval_style[i], anno_file=anno_file) + else: + logger.info("{} not exists!".format(v_json)) diff --git a/ppdet/utils/map_utils.py b/ppdet/utils/map_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..fe0530596bb54920a81235ee6716e2d43918899a --- /dev/null +++ b/ppdet/utils/map_utils.py @@ -0,0 +1,214 @@ +# 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 +from __future__ import unicode_literals + +import sys +import numpy as np +import logging +logger = logging.getLogger(__name__) + +__all__ = ['bbox_area', 'jaccard_overlap', 'DetectionMAP'] + + +def bbox_area(bbox, is_bbox_normalized): + """ + Calculate area of a bounding box + """ + norm = 1. - float(is_bbox_normalized) + width = bbox[2] - bbox[0] + norm + height = bbox[3] - bbox[1] + norm + return width * height + + +def jaccard_overlap(pred, gt, is_bbox_normalized=False): + """ + Calculate jaccard overlap ratio between two bounding box + """ + if pred[0] >= gt[2] or pred[2] <= gt[0] or \ + pred[1] >= gt[3] or pred[3] <= gt[1]: + return 0. + inter_xmin = max(pred[0], gt[0]) + inter_ymin = max(pred[1], gt[1]) + inter_xmax = min(pred[2], gt[2]) + inter_ymax = min(pred[3], gt[3]) + inter_size = bbox_area([inter_xmin, inter_ymin, inter_xmax, inter_ymax], + is_bbox_normalized) + pred_size = bbox_area(pred, is_bbox_normalized) + gt_size = bbox_area(gt, is_bbox_normalized) + overlap = float(inter_size) / (pred_size + gt_size - inter_size) + return overlap + + +class DetectionMAP(object): + """ + Calculate detection mean average precision. + Currently support two types: 11point and integral + + Args: + class_num (int): the class number. + overlap_thresh (float): The threshold of overlap + ratio between prediction bounding box and + ground truth bounding box for deciding + true/false positive. Default 0.5. + map_type (str): calculation method of mean average + precision, currently support '11point' and + 'integral'. Default '11point'. + is_bbox_normalized (bool): whther bounding boxes + is normalized to range[0, 1]. Default False. + evaluate_difficult (bool): whether to evaluate + difficult bounding boxes. Default False. + """ + + def __init__(self, + class_num, + overlap_thresh=0.5, + map_type='11point', + is_bbox_normalized=False, + evaluate_difficult=False): + self.class_num = class_num + self.overlap_thresh = overlap_thresh + assert map_type in ['11point', 'integral'], \ + "map_type currently only support '11point' "\ + "and 'integral'" + self.map_type = map_type + self.is_bbox_normalized = is_bbox_normalized + self.evaluate_difficult = evaluate_difficult + self.reset() + + def update(self, bbox, gt_box, gt_label, difficult=None): + """ + Update metric statics from given prediction and ground + truth infomations. + """ + if difficult is None: + difficult = np.zeros_like(gt_label) + + # record class gt count + for gtl, diff in zip(gt_label, difficult): + if self.evaluate_difficult or int(diff) == 0: + self.class_gt_counts[int(np.array(gtl))] += 1 + + # record class score positive + visited = [False] * len(gt_label) + for b in bbox: + label, score, xmin, ymin, xmax, ymax = b.tolist() + pred = [xmin, ymin, xmax, ymax] + max_idx = -1 + max_overlap = -1.0 + for i, gl in enumerate(gt_label): + if int(gl) == int(label): + overlap = jaccard_overlap(pred, gt_box[i], + self.is_bbox_normalized) + if overlap > max_overlap: + max_overlap = overlap + max_idx = i + + if max_overlap > self.overlap_thresh: + if self.evaluate_difficult or \ + int(np.array(difficult[max_idx])) == 0: + if not visited[max_idx]: + self.class_score_poss[int(label)].append([score, 1.0]) + visited[max_idx] = True + else: + self.class_score_poss[int(label)].append([score, 0.0]) + else: + self.class_score_poss[int(label)].append([score, 0.0]) + + def reset(self): + """ + Reset metric statics + """ + self.class_score_poss = [[] for _ in range(self.class_num)] + self.class_gt_counts = [0] * self.class_num + self.mAP = None + + def accumulate(self): + """ + Accumulate metric results and calculate mAP + """ + mAP = 0. + valid_cnt = 0 + for score_pos, count in zip(self.class_score_poss, + self.class_gt_counts): + if count == 0 or len(score_pos) == 0: + continue + + accum_tp_list, accum_fp_list = \ + self._get_tp_fp_accum(score_pos) + precision = [] + recall = [] + for ac_tp, ac_fp in zip(accum_tp_list, accum_fp_list): + precision.append(float(ac_tp) / (ac_tp + ac_fp)) + recall.append(float(ac_tp) / count) + + if self.map_type == '11point': + max_precisions = [0.] * 11 + start_idx = len(precision) - 1 + for j in range(10, -1, -1): + for i in range(start_idx, -1, -1): + if recall[i] < float(j) / 10.: + start_idx = i + if j > 0: + max_precisions[j - 1] = max_precisions[j] + break + else: + if max_precisions[j] < precision[i]: + max_precisions[j] = precision[i] + mAP += sum(max_precisions) / 11. + valid_cnt += 1 + elif self.map_type == 'integral': + import math + ap = 0. + prev_recall = 0. + for i in range(len(precision)): + recall_gap = math.fabs(recall[i] - prev_recall) + if recall_gap > 1e-6: + ap += precision[i] * recall_gap + prev_recall = recall[i] + mAP += ap + valid_cnt += 1 + else: + logger.error("Unspported mAP type {}".format(self.map_type)) + sys.exit(1) + + self.mAP = mAP / float(valid_cnt) if valid_cnt > 0 else mAP + + def get_map(self): + """ + Get mAP result + """ + if self.mAP is None: + logger.error("mAP is not calculated.") + return self.mAP + + def _get_tp_fp_accum(self, score_pos_list): + """ + Calculate accumulating true/false positive results from + [score, pos] records + """ + sorted_list = sorted(score_pos_list, key=lambda s: s[0], reverse=True) + accum_tp = 0 + accum_fp = 0 + accum_tp_list = [] + accum_fp_list = [] + for (score, pos) in sorted_list: + accum_tp += int(pos) + accum_tp_list.append(accum_tp) + accum_fp += 1 - int(pos) + accum_fp_list.append(accum_fp) + return accum_tp_list, accum_fp_list diff --git a/ppdet/utils/post_process.py b/ppdet/utils/post_process.py new file mode 100644 index 0000000000000000000000000000000000000000..cc80bc186626c4e983328ffd53f53d217230a244 --- /dev/null +++ b/ppdet/utils/post_process.py @@ -0,0 +1,212 @@ +# 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 numpy as np + +import paddle.fluid as fluid + +__all__ = ['nms'] + +logger = logging.getLogger(__name__) + + +def box_flip(boxes, im_shape): + im_width = im_shape[0][1] + flipped_boxes = boxes.copy() + + flipped_boxes[:, 0::4] = im_width - boxes[:, 2::4] - 1 + flipped_boxes[:, 2::4] = im_width - boxes[:, 0::4] - 1 + return flipped_boxes + + +def nms(dets, thresh): + """Apply classic DPM-style greedy NMS.""" + if dets.shape[0] == 0: + return [] + scores = dets[:, 0] + x1 = dets[:, 1] + y1 = dets[:, 2] + x2 = dets[:, 3] + y2 = dets[:, 4] + + areas = (x2 - x1 + 1) * (y2 - y1 + 1) + order = scores.argsort()[::-1] + + ndets = dets.shape[0] + suppressed = np.zeros((ndets), dtype=np.int) + + # nominal indices + # _i, _j + # sorted indices + # i, j + # temp variables for box i's (the box currently under consideration) + # ix1, iy1, ix2, iy2, iarea + + # variables for computing overlap with box j (lower scoring box) + # xx1, yy1, xx2, yy2 + # w, h + # inter, ovr + + for _i in range(ndets): + i = order[_i] + if suppressed[i] == 1: + continue + ix1 = x1[i] + iy1 = y1[i] + ix2 = x2[i] + iy2 = y2[i] + iarea = areas[i] + for _j in range(_i + 1, ndets): + j = order[_j] + if suppressed[j] == 1: + continue + xx1 = max(ix1, x1[j]) + yy1 = max(iy1, y1[j]) + xx2 = min(ix2, x2[j]) + yy2 = min(iy2, y2[j]) + w = max(0.0, xx2 - xx1 + 1) + h = max(0.0, yy2 - yy1 + 1) + inter = w * h + ovr = inter / (iarea + areas[j] - inter) + if ovr >= thresh: + suppressed[j] = 1 + + return np.where(suppressed == 0)[0] + + +def bbox_area(box): + w = box[2] - box[0] + 1 + h = box[3] - box[1] + 1 + return w * h + + +def bbox_overlaps(x, y): + N = x.shape[0] + K = y.shape[0] + overlaps = np.zeros((N, K), dtype=np.float32) + for k in range(K): + y_area = bbox_area(y[k]) + for n in range(N): + iw = min(x[n, 2], y[k, 2]) - max(x[n, 0], y[k, 0]) + 1 + if iw > 0: + ih = min(x[n, 3], y[k, 3]) - max(x[n, 1], y[k, 1]) + 1 + if ih > 0: + x_area = bbox_area(x[n]) + ua = x_area + y_area - iw * ih + overlaps[n, k] = iw * ih / ua + return overlaps + + +def box_voting(nms_dets, dets, vote_thresh): + top_dets = nms_dets.copy() + top_boxes = nms_dets[:, 1:] + all_boxes = dets[:, 1:] + all_scores = dets[:, 0] + top_to_all_overlaps = bbox_overlaps(top_boxes, all_boxes) + for k in range(nms_dets.shape[0]): + inds_to_vote = np.where(top_to_all_overlaps[k] >= vote_thresh)[0] + boxes_to_vote = all_boxes[inds_to_vote, :] + ws = all_scores[inds_to_vote] + top_dets[k, 1:] = np.average(boxes_to_vote, axis=0, weights=ws) + + return top_dets + + +def get_nms_result(boxes, scores, cfg): + cls_boxes = [[] for _ in range(cfg.num_classes)] + for j in range(1, cfg.num_classes): + inds = np.where(scores[:, j] > cfg.MultiScaleTEST['score_thresh'])[0] + scores_j = scores[inds, j] + boxes_j = boxes[inds, j * 4:(j + 1) * 4] + dets_j = np.hstack((scores_j[:, np.newaxis], boxes_j)).astype( + np.float32, copy=False) + keep = nms(dets_j, cfg.MultiScaleTEST['nms_thresh']) + nms_dets = dets_j[keep, :] + if cfg.MultiScaleTEST['enable_voting']: + nms_dets = box_voting(nms_dets, dets_j, + cfg.MultiScaleTEST['vote_thresh']) + #add labels + label = np.array([j for _ in range(len(keep))]) + nms_dets = np.hstack((label[:, np.newaxis], nms_dets)).astype( + np.float32, copy=False) + cls_boxes[j] = nms_dets + # Limit to max_per_image detections **over all classes** + image_scores = np.hstack( + [cls_boxes[j][:, 1] for j in range(1, cfg.num_classes)]) + if len(image_scores) > cfg.MultiScaleTEST['detections_per_im']: + image_thresh = np.sort(image_scores)[-cfg.MultiScaleTEST[ + 'detections_per_im']] + for j in range(1, cfg.num_classes): + keep = np.where(cls_boxes[j][:, 1] >= image_thresh)[0] + cls_boxes[j] = cls_boxes[j][keep, :] + + im_results = np.vstack([cls_boxes[j] for j in range(1, cfg.num_classes)]) + return im_results + + +def mstest_box_post_process(result, cfg): + """ + Multi-scale Test + Only available for batch_size=1 now. + """ + post_bbox = {} + use_flip = False + ms_boxes = [] + ms_scores = [] + im_shape = result['im_shape'][0] + for k in result.keys(): + if 'bbox' in k: + boxes = result[k][0] + boxes = np.reshape(boxes, (-1, 4 * cfg.num_classes)) + scores = result['score' + k[4:]][0] + if 'flip' in k: + boxes = box_flip(boxes, im_shape) + use_flip = True + ms_boxes.append(boxes) + ms_scores.append(scores) + + ms_boxes = np.concatenate(ms_boxes) + ms_scores = np.concatenate(ms_scores) + bbox_pred = get_nms_result(ms_boxes, ms_scores, cfg) + post_bbox.update({'bbox': (bbox_pred, [[len(bbox_pred)]])}) + if use_flip: + bbox = bbox_pred[:, 2:] + bbox_flip = np.append( + bbox_pred[:, :2], box_flip(bbox, im_shape), axis=1) + post_bbox.update({'bbox_flip': (bbox_flip, [[len(bbox_flip)]])}) + return post_bbox + + +def mstest_mask_post_process(result, cfg): + mask_list = [] + im_shape = result['im_shape'][0] + M = cfg.FPNRoIAlign['mask_resolution'] + for k in result.keys(): + if 'mask' in k: + masks = result[k][0] + if len(masks.shape) != 4: + masks = np.zeros((0, M, M)) + mask_list.append(masks) + continue + if 'flip' in k: + masks = masks[:, :, :, ::-1] + mask_list.append(masks) + + mask_pred = np.mean(mask_list, axis=0) + return {'mask': (mask_pred, [[len(mask_pred)]])} diff --git a/ppdet/utils/stats.py b/ppdet/utils/stats.py new file mode 100644 index 0000000000000000000000000000000000000000..4d7e36babf8d53170162cfd5581f591e376ec8cd --- /dev/null +++ b/ppdet/utils/stats.py @@ -0,0 +1,65 @@ +# 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 collections +import numpy as np +import datetime + +__all__ = ['TrainingStats', 'Time'] + + +class SmoothedValue(object): + """Track a series of values and provide access to smoothed values over a + window or the global series average. + """ + + def __init__(self, window_size): + self.deque = collections.deque(maxlen=window_size) + + def add_value(self, value): + self.deque.append(value) + + def get_median_value(self): + return np.median(self.deque) + + +def Time(): + return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f') + + +class TrainingStats(object): + def __init__(self, window_size, stats_keys): + self.smoothed_losses_and_metrics = { + key: SmoothedValue(window_size) + for key in stats_keys + } + + def update(self, stats): + for k, v in self.smoothed_losses_and_metrics.items(): + v.add_value(stats[k]) + + def get(self, extras=None): + stats = collections.OrderedDict() + if extras: + for k, v in extras.items(): + stats[k] = v + for k, v in self.smoothed_losses_and_metrics.items(): + stats[k] = format(v.get_median_value(), '.6f') + + return stats + + def log(self, extras=None): + d = self.get(extras) + strs = ', '.join(str(dict({x: y})).strip('{}') for x, y in d.items()) + return strs diff --git a/ppdet/utils/visualizer.py b/ppdet/utils/visualizer.py new file mode 100644 index 0000000000000000000000000000000000000000..ff35dc2d577fcaff042583d63c709a498dadb1da --- /dev/null +++ b/ppdet/utils/visualizer.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. + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import numpy as np +import pycocotools.mask as mask_util +from PIL import Image, ImageDraw + +from .colormap import colormap + +__all__ = ['visualize_results'] + + +def visualize_results(image, + im_id, + catid2name, + threshold=0.5, + bbox_results=None, + mask_results=None): + """ + Visualize bbox and mask results + """ + if mask_results: + image = draw_mask(image, im_id, mask_results, threshold) + if bbox_results: + image = draw_bbox(image, im_id, catid2name, bbox_results, + threshold) + return image + + +def draw_mask(image, im_id, segms, threshold, alpha=0.7): + """ + Draw mask on image + """ + mask_color_id = 0 + w_ratio = .4 + color_list = colormap(rgb=True) + img_array = np.array(image).astype('float32') + for dt in np.array(segms): + if im_id != dt['image_id']: + continue + segm, score = dt['segmentation'], dt['score'] + if score < threshold: + continue + mask = mask_util.decode(segm) * 255 + color_mask = color_list[mask_color_id % len(color_list), 0:3] + mask_color_id += 1 + for c in range(3): + color_mask[c] = color_mask[c] * (1 - w_ratio) + w_ratio * 255 + idx = np.nonzero(mask) + img_array[idx[0], idx[1], :] *= 1.0 - alpha + img_array[idx[0], idx[1], :] += alpha * color_mask + return Image.fromarray(img_array.astype('uint8')) + + +def draw_bbox(image, im_id, catid2name, bboxes, threshold): + """ + Draw bbox on image + """ + draw = ImageDraw.Draw(image) + + catid2color = {} + color_list = colormap(rgb=True)[:40] + for dt in np.array(bboxes): + if im_id != dt['image_id']: + continue + catid, bbox, score = dt['category_id'], dt['bbox'], dt['score'] + if score < threshold: + continue + + xmin, ymin, w, h = bbox + xmax = xmin + w + ymax = ymin + h + + if catid not in catid2color: + idx = np.random.randint(len(color_list)) + catid2color[catid] = color_list[idx] + color = tuple(catid2color[catid]) + + # draw bbox + draw.line( + [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin), + (xmin, ymin)], + width=2, + fill=color) + + # draw label + text = "{} {:.2f}".format(catid2name[catid], score) + tw, th = draw.textsize(text) + draw.rectangle([(xmin + 1, ymin - th), + (xmin + tw + 1, ymin)], + fill=color) + draw.text((xmin + 1, ymin - th), text, fill=(255, 255, 255)) + + return image diff --git a/ppdet/utils/voc_eval.py b/ppdet/utils/voc_eval.py new file mode 100644 index 0000000000000000000000000000000000000000..67e66e4c683aaf7d5af2fd352817daf1ad3576d4 --- /dev/null +++ b/ppdet/utils/voc_eval.py @@ -0,0 +1,186 @@ +# 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 +from __future__ import unicode_literals + +import os +import sys +import numpy as np + +from ..data.source.voc_loader import pascalvoc_label +from .map_utils import DetectionMAP +from .coco_eval import bbox2out + +import logging +logger = logging.getLogger(__name__) + +__all__ = [ + 'bbox_eval', 'bbox2out', 'get_category_info' +] + + +def bbox_eval(results, + class_num, + overlap_thresh=0.5, + map_type='11point', + is_bbox_normalized=False, + evaluate_difficult=False): + """ + Bounding box evaluation for VOC dataset + + Args: + results (list): prediction bounding box results. + class_num (int): evaluation class number. + overlap_thresh (float): the postive threshold of + bbox overlap + map_type (string): method for mAP calcualtion, + can only be '11point' or 'integral' + is_bbox_normalized (bool): whether bbox is normalized + to range [0, 1]. + evaluate_difficult (bool): whether to evaluate + difficult gt bbox. + """ + assert 'bbox' in results[0] + logger.info("Start evaluate...") + + detection_map = DetectionMAP(class_num=class_num, + overlap_thresh=overlap_thresh, + map_type=map_type, + is_bbox_normalized=is_bbox_normalized, + evaluate_difficult=evaluate_difficult) + + for t in results: + bboxes = t['bbox'][0] + bbox_lengths = t['bbox'][1][0] + + if bboxes.shape == (1, 1) or bboxes is None: + continue + + gt_boxes = t['gt_box'][0] + gt_labels = t['gt_label'][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 + bbox_idx = 0 + for i in range(len(gt_boxes)): + gt_box = gt_boxes[i] + gt_label = gt_labels[i] + difficult = None if difficults is None \ + else difficults[i] + bbox_num = bbox_lengths[i] + bbox = bboxes[bbox_idx: bbox_idx + bbox_num] + gt_box, gt_label, difficult = prune_zero_padding( + gt_box, gt_label, difficult) + detection_map.update(bbox, gt_box, gt_label, difficult) + bbox_idx += bbox_num + else: + # gt_box, gt_label, difficult read as LoDTensor + gt_box_lengths = t['gt_box'][1][0] + bbox_idx = 0 + gt_box_idx = 0 + for i in range(len(bbox_lengths)): + bbox_num = bbox_lengths[i] + gt_box_num = gt_box_lengths[i] + bbox = bboxes[bbox_idx: bbox_idx + bbox_num] + gt_box = gt_boxes[gt_box_idx: gt_box_idx + gt_box_num] + gt_label = gt_labels[gt_box_idx: gt_box_idx + gt_box_num] + difficult = None if difficults is None else \ + difficults[gt_box_idx: gt_box_idx + gt_box_num] + detection_map.update(bbox, gt_box, gt_label, difficult) + bbox_idx += bbox_num + gt_box_idx += gt_box_num + + logger.info("Accumulating evaluatation results...") + detection_map.accumulate() + map_stat = 100. * detection_map.get_map() + logger.info("mAP({:.2f}, {}) = {:.2f}".format(overlap_thresh, + map_type, map_stat)) + return map_stat + + +def prune_zero_padding(gt_box, gt_label, difficult=None): + valid_cnt = 0 + for i in range(len(gt_box)): + if gt_box[i, 0] == 0 and gt_box[i, 1] == 0 and \ + gt_box[i, 2] == 0 and gt_box[i, 3] == 0: + break + valid_cnt += 1 + return (gt_box[:valid_cnt], gt_label[:valid_cnt], + difficult[:valid_cnt] if difficult is not None else None) + + +def get_category_info(anno_file=None, + with_background=True, + use_default_label=False): + if use_default_label or anno_file is None \ + or not os.path.exists(anno_file): + logger.info("Not found annotation file {}, load " + "voc2012 categories.".format(anno_file)) + return vocall_category_info(with_background) + else: + logger.info("Load categories from {}".format(anno_file)) + return get_category_info_from_anno(anno_file, with_background) + + +def get_category_info_from_anno(anno_file, with_background=True): + """ + Get class id to category id map and category id + to category name map from annotation file. + + Args: + anno_file (str): annotation file path + with_background (bool, default True): + whether load background as class 0. + """ + cats = [] + with open(anno_file) as f: + for line in f.readlines(): + cats.append(line.strip()) + + if cats[0] != 'background' and with_background: + cats.insert(0, 'background') + if cats[0] == 'background' and not with_background: + cats = cats[1:] + + clsid2catid = {i: i for i in range(len(cats))} + catid2name = {i: name for i, name in enumerate(cats)} + + return clsid2catid, catid2name + + +def vocall_category_info(with_background=True): + """ + Get class id to category id map and category id + to category name map of mixup voc dataset + + Args: + with_background (bool, default True): + whether load background as class 0. + """ + label_map = pascalvoc_label(with_background) + label_map = sorted(label_map.items(), key=lambda x: x[1]) + cats = [l[0] for l in label_map] + + if with_background: + cats.insert(0, 'background') + + clsid2catid = {i: i for i in range(len(cats))} + catid2name = {i: name for i, name in enumerate(cats)} + + return clsid2catid, catid2name diff --git a/ppdet/utils/voc_utils.py b/ppdet/utils/voc_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..88252e2351164cf3402e94a2c0e6f49277d7736b --- /dev/null +++ b/ppdet/utils/voc_utils.py @@ -0,0 +1,86 @@ +# 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 os.path as osp +import re +import random +import shutil + +__all__ = ['create_list'] + + +def create_list(devkit_dir, years, output_dir): + """ + create following list: + 1. trainval.txt + 2. test.txt + """ + trainval_list = [] + test_list = [] + for year in years: + trainval, test = _walk_voc_dir(devkit_dir, year, output_dir) + trainval_list.extend(trainval) + test_list.extend(test) + + random.shuffle(trainval_list) + with open(osp.join(output_dir, 'trainval.txt'), 'w') as ftrainval: + for item in trainval_list: + ftrainval.write(item[0] + ' ' + item[1] + '\n') + + with open(osp.join(output_dir, 'test.txt'), 'w') as fval: + ct = 0 + for item in test_list: + ct += 1 + fval.write(item[0] + ' ' + item[1] + '\n') + + +def _get_voc_dir(devkit_dir, year, type): + return osp.join(devkit_dir, 'VOC' + year, type) + + +def _walk_voc_dir(devkit_dir, year, output_dir): + filelist_dir = _get_voc_dir(devkit_dir, year, 'ImageSets/Main') + annotation_dir = _get_voc_dir(devkit_dir, year, 'Annotations') + img_dir = _get_voc_dir(devkit_dir, year, 'JPEGImages') + trainval_list = [] + test_list = [] + added = set() + + for _, _, files in os.walk(filelist_dir): + for fname in files: + img_ann_list = [] + if re.match('[a-z]+_trainval\.txt', fname): + img_ann_list = trainval_list + elif re.match('[a-z]+_test\.txt', fname): + img_ann_list = test_list + else: + continue + fpath = osp.join(filelist_dir, fname) + for line in open(fpath): + name_prefix = line.strip().split()[0] + if name_prefix in added: + continue + added.add(name_prefix) + ann_path = osp.join(osp.relpath(annotation_dir, output_dir), + name_prefix + '.xml') + img_path = osp.join(osp.relpath(img_dir, output_dir), + name_prefix + '.jpg') + img_ann_list.append((img_path, ann_path)) + + return trainval_list, test_list diff --git a/ppdet/utils/widerface_eval_utils.py b/ppdet/utils/widerface_eval_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..a19cd08350e7be9b3c7db8aa45e95f86b7088bf5 --- /dev/null +++ b/ppdet/utils/widerface_eval_utils.py @@ -0,0 +1,227 @@ +# 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 + +from ppdet.data.source.widerface_loader import widerface_label +from ppdet.utils.coco_eval import bbox2out + +import logging +logger = logging.getLogger(__name__) + +__all__ = [ + 'get_shrink', 'bbox_vote', 'save_widerface_bboxes', 'save_fddb_bboxes', + 'to_chw_bgr', 'bbox2out', 'get_category_info' +] + + +def to_chw_bgr(image): + """ + Transpose image from HWC to CHW and from RBG to BGR. + Args: + image (np.array): an image with HWC and RBG layout. + """ + # HWC to CHW + if len(image.shape) == 3: + image = np.swapaxes(image, 1, 2) + image = np.swapaxes(image, 1, 0) + # RBG to BGR + image = image[[2, 1, 0], :, :] + return image + + +def bbox_vote(det): + order = det[:, 4].ravel().argsort()[::-1] + det = det[order, :] + if det.shape[0] == 0: + dets = np.array([[10, 10, 20, 20, 0.002]]) + det = np.empty(shape=[0, 5]) + while det.shape[0] > 0: + # IOU + area = (det[:, 2] - det[:, 0] + 1) * (det[:, 3] - det[:, 1] + 1) + xx1 = np.maximum(det[0, 0], det[:, 0]) + yy1 = np.maximum(det[0, 1], det[:, 1]) + xx2 = np.minimum(det[0, 2], det[:, 2]) + yy2 = np.minimum(det[0, 3], det[:, 3]) + w = np.maximum(0.0, xx2 - xx1 + 1) + h = np.maximum(0.0, yy2 - yy1 + 1) + inter = w * h + o = inter / (area[0] + area[:] - inter) + + # nms + merge_index = np.where(o >= 0.3)[0] + det_accu = det[merge_index, :] + det = np.delete(det, merge_index, 0) + if merge_index.shape[0] <= 1: + if det.shape[0] == 0: + try: + dets = np.row_stack((dets, det_accu)) + except: + dets = det_accu + continue + det_accu[:, 0:4] = det_accu[:, 0:4] * np.tile(det_accu[:, -1:], (1, 4)) + max_score = np.max(det_accu[:, 4]) + det_accu_sum = np.zeros((1, 5)) + det_accu_sum[:, 0:4] = np.sum(det_accu[:, 0:4], + axis=0) / np.sum(det_accu[:, -1:]) + det_accu_sum[:, 4] = max_score + try: + dets = np.row_stack((dets, det_accu_sum)) + except: + dets = det_accu_sum + dets = dets[0:750, :] + # Only keep 0.3 or more + keep_index = np.where(dets[:, 4] >= 0.01)[0] + dets = dets[keep_index, :] + return dets + + +def get_shrink(height, width): + """ + Args: + height (int): image height. + width (int): image width. + """ + # avoid out of memory + max_shrink_v1 = (0x7fffffff / 577.0 / (height * width))**0.5 + max_shrink_v2 = ((678 * 1024 * 2.0 * 2.0) / (height * width))**0.5 + + def get_round(x, loc): + str_x = str(x) + if '.' in str_x: + str_before, str_after = str_x.split('.') + len_after = len(str_after) + if len_after >= 3: + str_final = str_before + '.' + str_after[0:loc] + return float(str_final) + else: + return x + + max_shrink = get_round(min(max_shrink_v1, max_shrink_v2), 2) - 0.3 + if max_shrink >= 1.5 and max_shrink < 2: + max_shrink = max_shrink - 0.1 + elif max_shrink >= 2 and max_shrink < 3: + max_shrink = max_shrink - 0.2 + elif max_shrink >= 3 and max_shrink < 4: + max_shrink = max_shrink - 0.3 + elif max_shrink >= 4 and max_shrink < 5: + max_shrink = max_shrink - 0.4 + elif max_shrink >= 5: + max_shrink = max_shrink - 0.5 + + shrink = max_shrink if max_shrink < 1 else 1 + return shrink, max_shrink + + +def save_widerface_bboxes(image_path, bboxes_scores, output_dir): + image_name = image_path.split('/')[-1] + image_class = image_path.split('/')[-2] + odir = os.path.join(output_dir, image_class) + if not os.path.exists(odir): + os.makedirs(odir) + + ofname = os.path.join(odir, '%s.txt' % (image_name[:-4])) + f = open(ofname, 'w') + f.write('{:s}\n'.format(image_class + '/' + image_name)) + f.write('{:d}\n'.format(bboxes_scores.shape[0])) + for box_score in bboxes_scores: + xmin, ymin, xmax, ymax, score = box_score + f.write('{:.1f} {:.1f} {:.1f} {:.1f} {:.3f}\n'.format(xmin, ymin, ( + xmax - xmin + 1), (ymax - ymin + 1), score)) + f.close() + logger.info("The predicted result is saved as {}".format(ofname)) + + +def save_fddb_bboxes(bboxes_scores, + output_dir, + output_fname='pred_fddb_res.txt'): + if not os.path.exists(output_dir): + os.makedirs(output_dir) + predict_file = os.path.join(output_dir, output_fname) + f = open(predict_file, 'w') + for image_path, dets in bboxes_scores.iteritems(): + f.write('{:s}\n'.format(image_path)) + f.write('{:d}\n'.format(dets.shape[0])) + for box_score in dets: + xmin, ymin, xmax, ymax, score = box_score + width, height = xmax - xmin, ymax - ymin + f.write('{:.1f} {:.1f} {:.1f} {:.1f} {:.3f}\n' + .format(xmin, ymin, width, height, score)) + logger.info("The predicted result is saved as {}".format(predict_file)) + return predict_file + + +def get_category_info(anno_file=None, + with_background=True, + use_default_label=False): + if use_default_label or anno_file is None \ + or not os.path.exists(anno_file): + logger.info("Not found annotation file {}, load " + "wider-face categories.".format(anno_file)) + return widerfaceall_category_info(with_background) + else: + logger.info("Load categories from {}".format(anno_file)) + return get_category_info_from_anno(anno_file, with_background) + + +def get_category_info_from_anno(anno_file, with_background=True): + """ + Get class id to category id map and category id + to category name map from annotation file. + Args: + anno_file (str): annotation file path + with_background (bool, default True): + whether load background as class 0. + """ + cats = [] + with open(anno_file) as f: + for line in f.readlines(): + cats.append(line.strip()) + + if cats[0] != 'background' and with_background: + cats.insert(0, 'background') + if cats[0] == 'background' and not with_background: + cats = cats[1:] + + clsid2catid = {i: i for i in range(len(cats))} + catid2name = {i: name for i, name in enumerate(cats)} + + return clsid2catid, catid2name + + +def widerfaceall_category_info(with_background=True): + """ + Get class id to category id map and category id + to category name map of mixup wider_face dataset + + Args: + with_background (bool, default True): + whether load background as class 0. + """ + label_map = widerface_label(with_background) + label_map = sorted(label_map.items(), key=lambda x: x[1]) + cats = [l[0] for l in label_map] + + if with_background: + cats.insert(0, 'background') + + clsid2catid = {i: i for i in range(len(cats))} + catid2name = {i: name for i, name in enumerate(cats)} + + return clsid2catid, catid2name diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..798b006ad7baa3b4f24b4a7db15a63d6d2e533f4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +tqdm +docstring_parser @ http://github.com/willthefrog/docstring_parser/tarball/master +typeguard ; python_version >= '3.4' +tb-paddle +tb-nightly diff --git a/slim/distillation/README.md b/slim/distillation/README.md new file mode 100755 index 0000000000000000000000000000000000000000..e970cc42b54c17a6131c4873662fb2be46767b60 --- /dev/null +++ b/slim/distillation/README.md @@ -0,0 +1,141 @@ +>运行该示例前请安装Paddle1.6或更高版本 + +# 检测模型蒸馏示例 + +## 概述 + +该示例使用PaddleSlim提供的[蒸馏策略](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/tutorial.md#3-蒸馏)对检测库中的模型进行蒸馏训练。 +在阅读该示例前,建议您先了解以下内容: + +- [检测库的常规训练方法](https://github.com/PaddlePaddle/models/tree/develop/PaddleCV/PaddleDetection) +- [PaddleSlim使用文档](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/usage.md) + + +## 配置文件说明 + +关于配置文件如何编写您可以参考: + +- [PaddleSlim配置文件编写说明](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/usage.md#122-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E7%9A%84%E4%BD%BF%E7%94%A8) +- [蒸馏策略配置文件编写说明](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/usage.md#23-蒸馏) + +这里以ResNet34-YoloV3蒸馏MobileNetV1-YoloV3模型为例,首先,为了对`student model`和`teacher model`有个总体的认识,从而进一步确认蒸馏的对象,我们通过以下命令分别观察两个网络变量(Variable)的名称和形状: + +```python +# 观察student model的Variable +for v in fluid.default_main_program().list_vars(): + if "py_reader" not in v.name and "double_buffer" not in v.name and "generated_var" not in v.name: + print(v.name, v.shape) +# 观察teacher model的Variable +for v in teacher_program.list_vars(): + print(v.name, v.shape) +``` + +经过对比可以发现,`student model`和`teacher model`的部分中间结果分别为: + +```bash +# student model +conv2d_15.tmp_0 +# teacher model +teacher_teacher_conv2d_1.tmp_0 +``` + + +所以,我们用`l2_distiller`对这两个特征图做蒸馏。在配置文件中进行如下配置: + +```yaml +distillers: + l2_distiller: + class: 'L2Distiller' + teacher_feature_map: 'teacher_teacher_conv2d_1.tmp_0' + student_feature_map: 'conv2d_15.tmp_0' + distillation_loss_weight: 1 +strategies: + distillation_strategy: + class: 'DistillationStrategy' + distillers: ['l2_distiller'] + start_epoch: 0 + end_epoch: 270 +``` + +我们也可以根据上述操作为蒸馏策略选择其他loss,PaddleSlim支持的有`FSP_loss`, `L2_loss`和`softmax_with_cross_entropy_loss` 。 + +## 训练 + +根据[PaddleDetection/tools/train.py](https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/PaddleDetection/tools/train.py)编写压缩脚本compress.py。 +在该脚本中定义了Compressor对象,用于执行压缩任务。 + + + + +您可以通过运行脚本`run.sh`运行该示例。 + + +### 保存断点(checkpoint) + +如果在配置文件中设置了`checkpoint_path`, 则在蒸馏任务执行过程中会自动保存断点,当任务异常中断时, +重启任务会自动从`checkpoint_path`路径下按数字顺序加载最新的checkpoint文件。如果不想让重启的任务从断点恢复, +需要修改配置文件中的`checkpoint_path`,或者将`checkpoint_path`路径下文件清空。 + +>注意:配置文件中的信息不会保存在断点中,重启前对配置文件的修改将会生效。 + + +## 评估 + +如果在配置文件中设置了`checkpoint_path`,则每个epoch会保存一个压缩后的用于评估的模型, +该模型会保存在`${checkpoint_path}/${epoch_id}/eval_model/`路径下,包含`__model__`和`__params__`两个文件。 +其中,`__model__`用于保存模型结构信息,`__params__`用于保存参数(parameters)信息。 + +如果不需要保存评估模型,可以在定义Compressor对象时,将`save_eval_model`选项设置为False(默认为True)。 + +运行命令为: +``` +python ../eval.py \ + --model_path ${checkpoint_path}/${epoch_id}/eval_model/ \ + --model_name __model__ \ + --params_name __params__ \ + -c ../../configs/yolov3_mobilenet_v1_voc.yml \ + -d "../../dataset/voc" +``` + +## 预测 + +如果在配置文件中设置了`checkpoint_path`,并且在定义Compressor对象时指定了`prune_infer_model`选项,则每个epoch都会 +保存一个`inference model`。该模型是通过删除eval_program中多余的operators而得到的。 + +该模型会保存在`${checkpoint_path}/${epoch_id}/eval_model/`路径下,包含`__model__.infer`和`__params__`两个文件。 +其中,`__model__.infer`用于保存模型结构信息,`__params__`用于保存参数(parameters)信息。 + +更多关于`prune_infer_model`选项的介绍,请参考:[Compressor介绍](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/usage.md#121-%E5%A6%82%E4%BD%95%E6%94%B9%E5%86%99%E6%99%AE%E9%80%9A%E8%AE%AD%E7%BB%83%E8%84%9A%E6%9C%AC) + +### python预测 + +在脚本slim/infer.py中展示了如何使用fluid python API加载使用预测模型进行预测。 + +运行命令为: +``` +python ../infer.py \ + --model_path ${checkpoint_path}/${epoch_id}/eval_model/ \ + --model_name __model__.infer \ + --params_name __params__ \ + -c ../../configs/yolov3_mobilenet_v1_voc.yml \ + --infer_dir ../../demo +``` + +### PaddleLite + +该示例中产出的预测(inference)模型可以直接用PaddleLite进行加载使用。 +关于PaddleLite如何使用,请参考:[PaddleLite使用文档](https://github.com/PaddlePaddle/Paddle-Lite/wiki#%E4%BD%BF%E7%94%A8) + +## 示例结果 + +>当前release的结果并非超参调优后的最好结果,仅做示例参考,后续我们会优化当前结果。 + +### MobileNetV1-YOLO-V3 + +| FLOPS |Box AP| +|---|---| +|baseline|76.2 | +|蒸馏后|- | + + +## FAQ diff --git a/slim/distillation/compress.py b/slim/distillation/compress.py new file mode 100644 index 0000000000000000000000000000000000000000..6dc4fff9742ec258b30cad4408b5e6f428273495 --- /dev/null +++ b/slim/distillation/compress.py @@ -0,0 +1,324 @@ +# 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 time +import multiprocessing +import numpy as np +from collections import deque, OrderedDict +from paddle.fluid.contrib.slim.core import Compressor +from paddle.fluid.framework import IrGraph + + +def set_paddle_flags(**kwargs): + for key, value in kwargs.items(): + if os.environ.get(key, None) is None: + os.environ[key] = str(value) + + +# NOTE(paddle-dev): All of these flags should be set before +# `import paddle`. Otherwise, it would not take any effect. +set_paddle_flags( + FLAGS_eager_delete_tensor_gb=0, # enable GC to save memory +) + +from paddle import fluid + +import sys +sys.path.append("../../") +from ppdet.core.workspace import load_config, merge_config, create +from ppdet.data.data_feed import create_reader +from ppdet.utils.eval_utils import parse_fetches, eval_results +from ppdet.utils.stats import TrainingStats +from ppdet.utils.cli import ArgsParser +from ppdet.utils.check import check_gpu +import ppdet.utils.checkpoint as checkpoint +from ppdet.modeling.model_input import create_feed + +import logging +FORMAT = '%(asctime)s-%(levelname)s: %(message)s' +logging.basicConfig(level=logging.INFO, format=FORMAT) +logger = logging.getLogger(__name__) + + +def eval_run(exe, compile_program, reader, keys, values, cls, test_feed): + """ + Run evaluation program, return program outputs. + """ + iter_id = 0 + results = [] + if len(cls) != 0: + values = [] + for i in range(len(cls)): + _, accum_map = cls[i].get_map_var() + cls[i].reset(exe) + values.append(accum_map) + + images_num = 0 + start_time = time.time() + has_bbox = 'bbox' in keys + for data in reader(): + data = test_feed.feed(data) + feed_data = {'image': data['image'], 'im_size': data['im_size']} + outs = exe.run(compile_program, + feed=feed_data, + fetch_list=[values[0]], + return_numpy=False) + outs.append(data['gt_box']) + outs.append(data['gt_label']) + outs.append(data['is_difficult']) + res = { + k: (np.array(v), v.recursive_sequence_lengths()) + for k, v in zip(keys, outs) + } + results.append(res) + if iter_id % 100 == 0: + logger.info('Test iter {}'.format(iter_id)) + iter_id += 1 + images_num += len(res['bbox'][1][0]) if has_bbox else 1 + logger.info('Test finish iter {}'.format(iter_id)) + + end_time = time.time() + fps = images_num / (end_time - start_time) + if has_bbox: + logger.info('Total number of images: {}, inference time: {} fps.'. + format(images_num, fps)) + else: + logger.info('Total iteration: {}, inference time: {} batch/s.'.format( + images_num, fps)) + + return results + + +def main(): + cfg = load_config(FLAGS.config) + if 'architecture' in cfg: + main_arch = cfg.architecture + else: + raise ValueError("'architecture' not specified in config file.") + + merge_config(FLAGS.opt) + if 'log_iter' not in cfg: + cfg.log_iter = 20 + + # check if set use_gpu=True in paddlepaddle cpu version + check_gpu(cfg.use_gpu) + + if cfg.use_gpu: + devices_num = fluid.core.get_cuda_device_count() + else: + devices_num = int( + os.environ.get('CPU_NUM', multiprocessing.cpu_count())) + + if 'train_feed' not in cfg: + train_feed = create(main_arch + 'TrainFeed') + else: + train_feed = create(cfg.train_feed) + + if 'eval_feed' not in cfg: + eval_feed = create(main_arch + 'EvalFeed') + else: + eval_feed = create(cfg.eval_feed) + + place = fluid.CUDAPlace(0) if cfg.use_gpu else fluid.CPUPlace() + exe = fluid.Executor(place) + + lr_builder = create('LearningRate') + optim_builder = create('OptimizerBuilder') + + # build program + model = create(main_arch) + _, train_feed_vars = create_feed(train_feed, False) + train_fetches = model.train(train_feed_vars) + loss = train_fetches['loss'] + lr = lr_builder() + opt = optim_builder(lr) + opt.minimize(loss) + #for v in fluid.default_main_program().list_vars(): + # if "py_reader" not in v.name and "double_buffer" not in v.name and "generated_var" not in v.name: + # print(v.name, v.shape) + + cfg.max_iters = 258 + train_reader = create_reader(train_feed, cfg.max_iters, FLAGS.dataset_dir) + + exe.run(fluid.default_startup_program()) + + # parse train fetches + train_keys, train_values, _ = parse_fetches(train_fetches) + train_keys.append('lr') + train_values.append(lr.name) + + train_fetch_list = [] + for k, v in zip(train_keys, train_values): + train_fetch_list.append((k, v)) + print("train_fetch_list: {}".format(train_fetch_list)) + + eval_prog = fluid.Program() + startup_prog = fluid.Program() + with fluid.program_guard(eval_prog, startup_prog): + with fluid.unique_name.guard(): + model = create(main_arch) + _, test_feed_vars = create_feed(eval_feed, False) + fetches = model.eval(test_feed_vars) + eval_prog = eval_prog.clone(True) + + eval_reader = create_reader(eval_feed, args_path=FLAGS.dataset_dir) + test_data_feed = fluid.DataFeeder(test_feed_vars.values(), place) + + # parse eval fetches + 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'] + eval_keys, eval_values, eval_cls = parse_fetches(fetches, eval_prog, + extra_keys) + + eval_fetch_list = [] + for k, v in zip(eval_keys, eval_values): + eval_fetch_list.append((k, v)) + print("eval_fetch_list: {}".format(eval_fetch_list)) + + exe.run(startup_prog) + checkpoint.load_params(exe, + fluid.default_main_program(), cfg.pretrain_weights) + + best_box_ap_list = [] + + def eval_func(program, scope): + results = eval_run(exe, program, eval_reader, eval_keys, eval_values, + eval_cls, test_data_feed) + + resolution = None + is_bbox_normalized = False + 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) + if len(best_box_ap_list) == 0: + best_box_ap_list.append(box_ap_stats[0]) + elif box_ap_stats[0] > best_box_ap_list[0]: + best_box_ap_list[0] = box_ap_stats[0] + logger.info("Best test box ap: {}".format(best_box_ap_list[0])) + return best_box_ap_list[0] + + test_feed = [('image', test_feed_vars['image'].name), + ('im_size', test_feed_vars['im_size'].name)] + + teacher_cfg = load_config(FLAGS.teacher_config) + teacher_arch = teacher_cfg.architecture + teacher_programs = [] + teacher_program = fluid.Program() + teacher_startup_program = fluid.Program() + with fluid.program_guard(teacher_program, teacher_startup_program): + with fluid.unique_name.guard('teacher_'): + teacher_feed_vars = OrderedDict() + for name, var in train_feed_vars.items(): + teacher_feed_vars[name] = teacher_program.global_block( + )._clone_variable( + var, force_persistable=False) + model = create(teacher_arch) + train_fetches = model.train(teacher_feed_vars) + #print("="*50+"teacher_model_params"+"="*50) + #for v in teacher_program.list_vars(): + # print(v.name, v.shape) + #return + + exe.run(teacher_startup_program) + assert FLAGS.teacher_pretrained and os.path.exists( + FLAGS.teacher_pretrained + ), "teacher_pretrained should be set when teacher_model is not None." + + def if_exist(var): + return os.path.exists(os.path.join(FLAGS.teacher_pretrained, var.name)) + + fluid.io.load_vars( + exe, + FLAGS.teacher_pretrained, + main_program=teacher_program, + predicate=if_exist) + + teacher_programs.append(teacher_program.clone(for_test=True)) + + com = Compressor( + place, + fluid.global_scope(), + fluid.default_main_program(), + train_reader=train_reader, + train_feed_list=[(key, value.name) + for key, value in train_feed_vars.items()], + train_fetch_list=train_fetch_list, + eval_program=eval_prog, + eval_reader=eval_reader, + eval_feed_list=test_feed, + eval_func={'map': eval_func}, + eval_fetch_list=eval_fetch_list[0:1], + save_eval_model=True, + prune_infer_model=[["image", "im_size"], ["multiclass_nms_0.tmp_0"]], + teacher_programs=teacher_programs, + train_optimizer=None, + distiller_optimizer=opt, + log_period=20) + com.config(FLAGS.slim_file) + com.run() + + +if __name__ == '__main__': + parser = ArgsParser() + parser.add_argument( + "-t", + "--teacher_config", + default=None, + type=str, + help="Config file of teacher architecture.") + parser.add_argument( + "-s", + "--slim_file", + default=None, + type=str, + help="Config file of PaddleSlim.") + parser.add_argument( + "-r", + "--resume_checkpoint", + default=None, + type=str, + help="Checkpoint path for resuming training.") + parser.add_argument( + "--eval", + action='store_true', + default=False, + help="Whether to perform evaluation in train") + parser.add_argument( + "--teacher_pretrained", + default=None, + type=str, + help="Whether to use pretrained model.") + parser.add_argument( + "--output_eval", + 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") + FLAGS = parser.parse_args() + main() diff --git a/slim/distillation/run.sh b/slim/distillation/run.sh new file mode 100644 index 0000000000000000000000000000000000000000..a5497bdce2464c72e14fa2168b87db60685e83e8 --- /dev/null +++ b/slim/distillation/run.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +# download pretrain model +root_url="https://paddlemodels.bj.bcebos.com/object_detection" +yolov3_r34_voc="yolov3_r34_voc.tar" +pretrain_dir='./pretrain' + +if [ ! -d ${pretrain_dir} ]; then + mkdir ${pretrain_dir} +fi + +cd ${pretrain_dir} + +if [ ! -f ${yolov3_r34_voc} ]; then + wget ${root_url}/${yolov3_r34_voc} + tar xf ${yolov3_r34_voc} +fi +cd - + +# enable GC strategy +export FLAGS_fast_eager_deletion_mode=1 +export FLAGS_eager_delete_tensor_gb=0.0 + +# for distillation +#----------------- +export CUDA_VISIBLE_DEVICES=0,1,2,3 + + +# Fixing name conflicts in distillation +cd ${pretrain_dir}/yolov3_r34_voc +for files in $(ls teacher_*) + do mv $files ${files#*_} +done +for files in $(ls *) + do mv $files "teacher_"$files +done +cd - + +python -u compress.py \ +-c ../../configs/yolov3_mobilenet_v1_voc.yml \ +-t yolov3_resnet34.yml \ +-s yolov3_mobilenet_v1_yolov3_resnet34_distillation.yml \ +-o YoloTrainFeed.batch_size=64 \ +-d ../../dataset/voc \ +--teacher_pretrained ./pretrain/yolov3_r34_voc \ +> yolov3_distallation.log 2>&1 & +tailf yolov3_distallation.log diff --git a/slim/distillation/yolov3_mobilenet_v1_yolov3_resnet34_distillation.yml b/slim/distillation/yolov3_mobilenet_v1_yolov3_resnet34_distillation.yml new file mode 100644 index 0000000000000000000000000000000000000000..6a2a5a2575b71563c557c528a4dc94f00dce73ca --- /dev/null +++ b/slim/distillation/yolov3_mobilenet_v1_yolov3_resnet34_distillation.yml @@ -0,0 +1,18 @@ +version: 1.0 +distillers: + l2_distiller: + class: 'L2Distiller' + teacher_feature_map: 'teacher_teacher_conv2d_1.tmp_0' + student_feature_map: 'conv2d_15.tmp_0' + distillation_loss_weight: 1 +strategies: + distillation_strategy: + class: 'DistillationStrategy' + distillers: ['l2_distiller'] + start_epoch: 0 + end_epoch: 270 +compressor: + epoch: 271 + checkpoint_path: './checkpoints/' + strategies: + - distillation_strategy diff --git a/slim/distillation/yolov3_resnet34.yml b/slim/distillation/yolov3_resnet34.yml new file mode 100644 index 0000000000000000000000000000000000000000..c04bdde9f6e35a1ce53231e3862a37364ff7dbb8 --- /dev/null +++ b/slim/distillation/yolov3_resnet34.yml @@ -0,0 +1,34 @@ +architecture: YOLOv3 +log_smooth_window: 20 +metric: VOC +map_type: 11point +num_classes: 20 +weight_prefix_name: teacher_ + +YOLOv3: + backbone: ResNet + yolo_head: YOLOv3Head + +ResNet: + norm_type: sync_bn + freeze_at: 0 + freeze_norm: false + norm_decay: 0. + depth: 34 + feature_maps: [3, 4, 5] + +YOLOv3Head: + anchor_masks: [[6, 7, 8], [3, 4, 5], [0, 1, 2]] + anchors: [[10, 13], [16, 30], [33, 23], + [30, 61], [62, 45], [59, 119], + [116, 90], [156, 198], [373, 326]] + norm_decay: 0. + ignore_thresh: 0.7 + label_smooth: false + nms: + background_label: -1 + keep_top_k: 100 + nms_threshold: 0.45 + nms_top_k: 1000 + normalized: false + score_threshold: 0.01 diff --git a/slim/eval.py b/slim/eval.py new file mode 100644 index 0000000000000000000000000000000000000000..579f58d2932e215362efa9e8864c16d1a451b26a --- /dev/null +++ b/slim/eval.py @@ -0,0 +1,194 @@ +# 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 time +import multiprocessing +import numpy as np +import datetime +from collections import deque +import sys +sys.path.append("../../") +from paddle.fluid.contrib.slim import Compressor +from paddle.fluid.framework import IrGraph +from paddle.fluid import core +from paddle.fluid.contrib.slim.quantization import QuantizationTransformPass +from paddle.fluid.contrib.slim.quantization import QuantizationFreezePass +from paddle.fluid.contrib.slim.quantization import ConvertToInt8Pass +from paddle.fluid.contrib.slim.quantization import TransformForMobilePass + + +def set_paddle_flags(**kwargs): + for key, value in kwargs.items(): + if os.environ.get(key, None) is None: + os.environ[key] = str(value) + + +# NOTE(paddle-dev): All of these flags should be set before +# `import paddle`. Otherwise, it would not take any effect. +set_paddle_flags( + FLAGS_eager_delete_tensor_gb=0, # enable GC to save memory +) + +from paddle import fluid + +from ppdet.core.workspace import load_config, merge_config, create +from ppdet.data.data_feed import create_reader + +from ppdet.utils.eval_utils import parse_fetches, eval_results +from ppdet.utils.stats import TrainingStats +from ppdet.utils.cli import ArgsParser +from ppdet.utils.check import check_gpu +import ppdet.utils.checkpoint as checkpoint +from ppdet.modeling.model_input import create_feed + +import logging +FORMAT = '%(asctime)s-%(levelname)s: %(message)s' +logging.basicConfig(level=logging.INFO, format=FORMAT) +logger = logging.getLogger(__name__) + + +def eval_run(exe, compile_program, reader, keys, values, cls, test_feed): + """ + Run evaluation program, return program outputs. + """ + iter_id = 0 + results = [] + + images_num = 0 + start_time = time.time() + has_bbox = 'bbox' in keys + for data in reader(): + data = test_feed.feed(data) + feed_data = {'image': data['image'], 'im_size': data['im_size']} + outs = exe.run(compile_program, + feed=feed_data, + fetch_list=values[0], + return_numpy=False) + outs.append(data['gt_box']) + outs.append(data['gt_label']) + outs.append(data['is_difficult']) + res = { + k: (np.array(v), v.recursive_sequence_lengths()) + for k, v in zip(keys, outs) + } + results.append(res) + if iter_id % 100 == 0: + logger.info('Test iter {}'.format(iter_id)) + iter_id += 1 + images_num += len(res['bbox'][1][0]) if has_bbox else 1 + logger.info('Test finish iter {}'.format(iter_id)) + + end_time = time.time() + fps = images_num / (end_time - start_time) + if has_bbox: + logger.info('Total number of images: {}, inference time: {} fps.'. + format(images_num, fps)) + else: + logger.info('Total iteration: {}, inference time: {} batch/s.'.format( + images_num, fps)) + + return results + + +def main(): + cfg = load_config(FLAGS.config) + if 'architecture' in cfg: + main_arch = cfg.architecture + else: + raise ValueError("'architecture' not specified in config file.") + + merge_config(FLAGS.opt) + if 'log_iter' not in cfg: + cfg.log_iter = 20 + + # check if set use_gpu=True in paddlepaddle cpu version + check_gpu(cfg.use_gpu) + + if cfg.use_gpu: + devices_num = fluid.core.get_cuda_device_count() + else: + devices_num = int( + os.environ.get('CPU_NUM', multiprocessing.cpu_count())) + + if 'eval_feed' not in cfg: + eval_feed = create(main_arch + 'EvalFeed') + else: + eval_feed = create(cfg.eval_feed) + + place = fluid.CUDAPlace(0) if cfg.use_gpu else fluid.CPUPlace() + exe = fluid.Executor(place) + + _, test_feed_vars = create_feed(eval_feed, False) + + eval_reader = create_reader(eval_feed, args_path=FLAGS.dataset_dir) + #eval_pyreader.decorate_sample_list_generator(eval_reader, place) + test_data_feed = fluid.DataFeeder(test_feed_vars.values(), place) + + assert os.path.exists(FLAGS.model_path) + infer_prog, feed_names, fetch_targets = fluid.io.load_inference_model( + dirname=FLAGS.model_path, + executor=exe, + model_filename=FLAGS.model_name, + params_filename=FLAGS.params_name) + + eval_keys = ['bbox', 'gt_box', 'gt_label', 'is_difficult'] + eval_values = [ + 'multiclass_nms_0.tmp_0', 'gt_box', 'gt_label', 'is_difficult' + ] + eval_cls = [] + eval_values[0] = fetch_targets[0] + + results = eval_run(exe, infer_prog, eval_reader, eval_keys, eval_values, + eval_cls, test_data_feed) + + resolution = None + if 'mask' in results[0]: + resolution = model.mask_head.resolution + eval_results(results, eval_feed, cfg.metric, cfg.num_classes, resolution, + False, FLAGS.output_eval) + + +if __name__ == '__main__': + parser = ArgsParser() + parser.add_argument( + "-m", "--model_path", default=None, type=str, help="path of checkpoint") + parser.add_argument( + "--output_eval", + 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( + "--model_name", + default='model', + type=str, + help="model file name to load_inference_model") + parser.add_argument( + "--params_name", + default='params', + type=str, + help="params file name to load_inference_model") + + FLAGS = parser.parse_args() + main() diff --git a/slim/infer.py b/slim/infer.py new file mode 100644 index 0000000000000000000000000000000000000000..a5c00de983a15c40db60deb9097c3d8f92a557aa --- /dev/null +++ b/slim/infer.py @@ -0,0 +1,300 @@ +# 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 sys +import glob +import time + +import numpy as np +from PIL import Image +sys.path.append("../../") + + +def set_paddle_flags(**kwargs): + for key, value in kwargs.items(): + if os.environ.get(key, None) is None: + os.environ[key] = str(value) + + +# NOTE(paddle-dev): All of these flags should be set before +# `import paddle`. Otherwise, it would not take any effect. +set_paddle_flags( + FLAGS_eager_delete_tensor_gb=0, # enable GC to save memory +) + +from paddle import fluid +from ppdet.utils.cli import print_total_cfg +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 +from ppdet.utils.check import check_gpu +from ppdet.utils.visualizer import visualize_results +import ppdet.utils.checkpoint as checkpoint + +import logging +FORMAT = '%(asctime)s-%(levelname)s: %(message)s' +logging.basicConfig(level=logging.INFO, format=FORMAT) +logger = logging.getLogger(__name__) + + +def get_save_image_name(output_dir, image_path): + """ + Get save image name from source image path. + """ + if not os.path.exists(output_dir): + os.makedirs(output_dir) + image_name = os.path.split(image_path)[-1] + name, ext = os.path.splitext(image_name) + return os.path.join(output_dir, "{}".format(name)) + ext + + +def get_test_images(infer_dir, infer_img): + """ + Get image path list in TEST mode + """ + assert infer_img is not None or infer_dir is not None, \ + "--infer_img or --infer_dir should be set" + assert infer_img is None or os.path.isfile(infer_img), \ + "{} is not a file".format(infer_img) + assert infer_dir is None or os.path.isdir(infer_dir), \ + "{} is not a directory".format(infer_dir) + images = [] + + # infer_img has a higher priority + if infer_img and os.path.isfile(infer_img): + images.append(infer_img) + return images + + infer_dir = os.path.abspath(infer_dir) + assert os.path.isdir(infer_dir), \ + "infer_dir {} is not a directory".format(infer_dir) + exts = ['jpg', 'jpeg', 'png', 'bmp'] + exts += [ext.upper() for ext in exts] + for ext in exts: + images.extend(glob.glob('{}/*.{}'.format(infer_dir, ext))) + + assert len(images) > 0, "no image found in {}".format(infer_dir) + logger.info("Found {} inference images in total.".format(len(images))) + + return images + + +def main(): + cfg = load_config(FLAGS.config) + + if 'architecture' in cfg: + main_arch = cfg.architecture + else: + raise ValueError("'architecture' not specified in config file.") + + merge_config(FLAGS.opt) + + # check if set use_gpu=True in paddlepaddle cpu version + check_gpu(cfg.use_gpu) + # print_total_cfg(cfg) + + if 'test_feed' not in cfg: + test_feed = create(main_arch + 'TestFeed') + else: + test_feed = create(cfg.test_feed) + + test_images = get_test_images(FLAGS.infer_dir, FLAGS.infer_img) + test_feed.dataset.add_images(test_images) + + place = fluid.CUDAPlace(0) if cfg.use_gpu else fluid.CPUPlace() + exe = fluid.Executor(place) + + infer_prog, feed_var_names, fetch_list = fluid.io.load_inference_model( + dirname=FLAGS.model_path, + model_filename=FLAGS.model_name, + params_filename=FLAGS.params_name, + executor=exe) + + reader = create_reader(test_feed) + feeder = fluid.DataFeeder( + place=place, feed_list=feed_var_names, program=infer_prog) + + # parse infer fetches + 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 = ['im_id', 'im_shape'] + keys, values, _ = parse_fetches({ + 'bbox': fetch_list + }, infer_prog, extra_keys) + + # parse dataset category + if cfg.metric == 'COCO': + from ppdet.utils.coco_eval import bbox2out, mask2out, get_category_info + if cfg.metric == "VOC": + from ppdet.utils.voc_eval 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) + clsid2catid, catid2name = get_category_info(anno_file, with_background, + use_default_label) + + # whether output bbox is normalized in model output layer + is_bbox_normalized = False + + # use tb-paddle to log image + if FLAGS.use_tb: + from tb_paddle import SummaryWriter + tb_writer = SummaryWriter(FLAGS.tb_log_dir) + tb_image_step = 0 + tb_image_frame = 0 # each frame can display ten pictures at most. + + imid2path = reader.imid2path + keys = ['bbox'] + infer_time = True + compile_prog = fluid.compiler.CompiledProgram(infer_prog) + + for iter_id, data in enumerate(reader()): + feed_data = [[d[0], d[1]] for d in data] + # for infer time + if infer_time: + warmup_times = 10 + repeats_time = 100 + feed_data_dict = feeder.feed(feed_data) + for i in range(warmup_times): + exe.run(compile_prog, + feed=feed_data_dict, + fetch_list=fetch_list, + return_numpy=False) + start_time = time.time() + for i in range(repeats_time): + exe.run(compile_prog, + feed=feed_data_dict, + fetch_list=fetch_list, + return_numpy=False) + + print("infer time: {} ms/sample".format((time.time() - start_time) * + 1000 / repeats_time)) + infer_time = False + + outs = exe.run(compile_prog, + feed=feeder.feed(feed_data), + fetch_list=fetch_list, + return_numpy=False) + res = { + k: (np.array(v), v.recursive_sequence_lengths()) + for k, v in zip(keys, outs) + } + res['im_id'] = [[d[2] for d in data]] + logger.info('Infer iter {}'.format(iter_id)) + + bbox_results = None + mask_results = None + if 'bbox' in res: + bbox_results = bbox2out([res], clsid2catid, is_bbox_normalized) + if 'mask' in res: + mask_results = mask2out([res], clsid2catid, + model.mask_head.resolution) + + # visualize result + im_ids = res['im_id'][0] + for im_id in im_ids: + image_path = imid2path[int(im_id)] + image = Image.open(image_path).convert('RGB') + + # use tb-paddle to log original image + if FLAGS.use_tb: + original_image_np = np.array(image) + tb_writer.add_image( + "original/frame_{}".format(tb_image_frame), + original_image_np, + tb_image_step, + dataformats='HWC') + + image = visualize_results(image, + int(im_id), catid2name, + FLAGS.draw_threshold, bbox_results, + mask_results) + + # use tb-paddle to log image with bbox + if FLAGS.use_tb: + infer_image_np = np.array(image) + tb_writer.add_image( + "bbox/frame_{}".format(tb_image_frame), + infer_image_np, + tb_image_step, + dataformats='HWC') + tb_image_step += 1 + if tb_image_step % 10 == 0: + tb_image_step = 0 + tb_image_frame += 1 + + save_name = get_save_image_name(FLAGS.output_dir, image_path) + logger.info("Detection bbox results save in {}".format(save_name)) + image.save(save_name, quality=95) + + +if __name__ == '__main__': + parser = ArgsParser() + parser.add_argument( + "--infer_dir", + type=str, + default=None, + help="Directory for images to perform inference on.") + parser.add_argument( + "--infer_img", + type=str, + default=None, + help="Image path, has higher priority over --infer_dir") + parser.add_argument( + "--output_dir", + type=str, + default="output", + help="Directory for storing the output visualization files.") + parser.add_argument( + "--draw_threshold", + type=float, + default=0.5, + help="Threshold to reserve the result for visualization.") + parser.add_argument( + "--use_tb", + type=bool, + default=False, + help="whether to record the data to Tensorboard.") + parser.add_argument( + '--tb_log_dir', + type=str, + default="tb_log_dir/image", + help='Tensorboard logging directory for image.') + parser.add_argument( + '--model_path', type=str, default=None, help="inference model path") + parser.add_argument( + '--model_name', + type=str, + default='__model__.infer', + help="model filename for inference model") + parser.add_argument( + '--params_name', + type=str, + default='__params__', + help="params filename for inference model") + FLAGS = parser.parse_args() + main() diff --git a/slim/prune/README.md b/slim/prune/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b06fdd2bdd6a3cd75eb00ab7952dfd546b2bfaad --- /dev/null +++ b/slim/prune/README.md @@ -0,0 +1,221 @@ +>运行该示例前请安装Paddle1.6或更高版本 + +# 检测模型卷积通道剪裁示例 + +## 概述 + +该示例使用PaddleSlim提供的[卷积通道剪裁压缩策略](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/tutorial.md#2-%E5%8D%B7%E7%A7%AF%E6%A0%B8%E5%89%AA%E8%A3%81%E5%8E%9F%E7%90%86)对检测库中的模型进行压缩。 +在阅读该示例前,建议您先了解以下内容: + +- 检测库的常规训练方法 +- [检测模型数据准备](https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/PaddleDetection/docs/INSTALL_cn.md#%E6%95%B0%E6%8D%AE%E9%9B%86) +- [PaddleSlim使用文档](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/usage.md) + + +## 配置文件说明 + +关于配置文件如何编写您可以参考: + +- [PaddleSlim配置文件编写说明](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/usage.md#122-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E7%9A%84%E4%BD%BF%E7%94%A8) +- [裁剪策略配置文件编写说明](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/usage.md#22-%E6%A8%A1%E5%9E%8B%E9%80%9A%E9%81%93%E5%89%AA%E8%A3%81) + +其中,配置文件中的`pruned_params`需要根据当前模型的网络结构特点设置,它用来指定要裁剪的parameters. + +这里以MobileNetV1-YoloV3模型为例,其卷积可以三种:主干网络中的普通卷积,主干网络中的`depthwise convolution`和`yolo block`里的普通卷积。PaddleSlim暂时无法对`depthwise convolution`直接进行剪裁, 因为`depthwise convolution`的`channel`的变化会同时影响到前后的卷积层。我们这里只对主干网络中的普通卷积和`yolo block`里的普通卷积做裁剪。 + +通过以下方式可视化模型结构: + +``` +from paddle.fluid.framework import IrGraph +from paddle.fluid import core + +graph = IrGraph(core.Graph(train_prog.desc), for_test=True) +marked_nodes = set() +for op in graph.all_op_nodes(): + print(op.name()) + if op.name().find('conv') > -1: + marked_nodes.add(op) +graph.draw('.', 'forward', marked_nodes) +``` + +该示例中MobileNetV1-YoloV3模型结构的可视化结果:MobileNetV1-YoloV3.pdf + +同时通过以下命令观察目标卷积层的参数(parameters)的名称和shape: + +``` +for param in fluid.default_main_program().global_block().all_parameters(): + if 'weights' in param.name: + print(param.name, param.shape) +``` + + +从可视化结果,我们可以排除后续会做concat的卷积层,最终得到如下要裁剪的参数名称: + +``` +conv2_1_sep_weights +conv2_2_sep_weights +conv3_1_sep_weights +conv4_1_sep_weights +conv5_1_sep_weights +conv5_2_sep_weights +conv5_3_sep_weights +conv5_4_sep_weights +conv5_5_sep_weights +conv5_6_sep_weights +yolo_block.0.0.0.conv.weights +yolo_block.0.0.1.conv.weights +yolo_block.0.1.0.conv.weights +yolo_block.0.1.1.conv.weights +yolo_block.1.0.0.conv.weights +yolo_block.1.0.1.conv.weights +yolo_block.1.1.0.conv.weights +yolo_block.1.1.1.conv.weights +yolo_block.1.2.conv.weights +yolo_block.2.0.0.conv.weights +yolo_block.2.0.1.conv.weights +yolo_block.2.1.1.conv.weights +yolo_block.2.2.conv.weights +yolo_block.2.tip.conv.weights +``` + +``` +(conv2_1_sep_weights)|(conv2_2_sep_weights)|(conv3_1_sep_weights)|(conv4_1_sep_weights)|(conv5_1_sep_weights)|(conv5_2_sep_weights)|(conv5_3_sep_weights)|(conv5_4_sep_weights)|(conv5_5_sep_weights)|(conv5_6_sep_weights)|(yolo_block.0.0.0.conv.weights)|(yolo_block.0.0.1.conv.weights)|(yolo_block.0.1.0.conv.weights)|(yolo_block.0.1.1.conv.weights)|(yolo_block.1.0.0.conv.weights)|(yolo_block.1.0.1.conv.weights)|(yolo_block.1.1.0.conv.weights)|(yolo_block.1.1.1.conv.weights)|(yolo_block.1.2.conv.weights)|(yolo_block.2.0.0.conv.weights)|(yolo_block.2.0.1.conv.weights)|(yolo_block.2.1.1.conv.weights)|(yolo_block.2.2.conv.weights)|(yolo_block.2.tip.conv.weights) +``` + +综上,我们将MobileNetV2配置文件中的`pruned_params`设置为以下正则表达式: + +``` +(conv2_1_sep_weights)|(conv2_2_sep_weights)|(conv3_1_sep_weights)|(conv4_1_sep_weights)|(conv5_1_sep_weights)|(conv5_2_sep_weights)|(conv5_3_sep_weights)|(conv5_4_sep_weights)|(conv5_5_sep_weights)|(conv5_6_sep_weights)|(yolo_block.0.0.0.conv.weights)|(yolo_block.0.0.1.conv.weights)|(yolo_block.0.1.0.conv.weights)|(yolo_block.0.1.1.conv.weights)|(yolo_block.1.0.0.conv.weights)|(yolo_block.1.0.1.conv.weights)|(yolo_block.1.1.0.conv.weights)|(yolo_block.1.1.1.conv.weights)|(yolo_block.1.2.conv.weights)|(yolo_block.2.0.0.conv.weights)|(yolo_block.2.0.1.conv.weights)|(yolo_block.2.1.1.conv.weights)|(yolo_block.2.2.conv.weights)|(yolo_block.2.tip.conv.weights) +``` + +我们可以用上述操作观察其它检测模型的参数名称规律,然后设置合适的正则表达式来剪裁合适的参数。 + +## 训练 + +根据PaddleDetection/tools/train.py编写压缩脚本compress.py。 +在该脚本中定义了Compressor对象,用于执行压缩任务。 + +### 执行示例 + +step1: 设置gpu卡 +``` +export CUDA_VISIBLE_DEVICES=0 +``` +step2: 开始训练 + +使用PaddleDetection提供的配置文件在用8卡进行训练: + +``` +python compress.py \ + -s yolov3_mobilenet_v1_slim.yaml \ + -c ../../configs/yolov3_mobilenet_v1_voc.yml \ + -o max_iters=258 \ + YoloTrainFeed.batch_size=64 \ + -d "../../dataset/voc" +``` + +>通过命令行覆盖设置max_iters选项,因为PaddleDetection中训练是以`batch`为单位迭代的,并没有涉及`epoch`的概念,但是PaddleSlim需要知道当前训练进行到第几个`epoch`, 所以需要将`max_iters`设置为一个`epoch`内的`batch`的数量。 + +如果要调整训练卡数,需要调整配置文件`yolov3_mobilenet_v1_voc.yml`中的以下参数: + +- **max_iters:** 一个`epoch`中batch的数量,需要设置为`total_num / batch_size`, 其中`total_num`为训练样本总数量,`batch_size`为多卡上总的batch size. +- **YoloTrainFeed.batch_size:** 当使用DataLoader时,表示单张卡上的batch size; 当使用普通reader时,则表示多卡上的总的`batch_size`。`batch_size`受限于显存大小。 +- **LeaningRate.base_lr:** 根据多卡的总`batch_size`调整`base_lr`,两者大小正相关,可以简单的按比例进行调整。 +- **LearningRate.schedulers.PiecewiseDecay.milestones:** 请根据batch size的变化对其调整。 +- **LearningRate.schedulers.PiecewiseDecay.LinearWarmup.steps:** 请根据batch size的变化对其进行调整。 + + +以下为4卡训练示例,通过命令行覆盖`yolov3_mobilenet_v1_voc.yml`中的参数: + +``` +python compress.py \ + -s yolov3_mobilenet_v1_slim.yaml \ + -c ../../configs/yolov3_mobilenet_v1_voc.yml \ + -o max_iters=258 \ + YoloTrainFeed.batch_size=64 \ + -d "../../dataset/voc" +``` + +以下为2卡训练示例,受显存所制,单卡`batch_size`不变,总`batch_size`减小,`base_lr`减小,一个epoch内batch数量增加,同时需要调整学习率相关参数,如下: +``` +python compress.py \ + -s yolov3_mobilenet_v1_slim.yaml \ + -c ../../configs/yolov3_mobilenet_v1_voc.yml \ + -o max_iters=516 \ + LeaningRate.base_lr=0.005 \ + YoloTrainFeed.batch_size=32 \ + LearningRate.schedulers='[!PiecewiseDecay {gamma: 0.1, milestones: [110000, 124000]}, !LinearWarmup {start_factor: 0., steps: 2000}]' \ + -d "../../dataset/voc" +``` + +通过`python compress.py --help`查看可配置参数。 +通过`python ../../tools/configure.py ${option_name} help`查看如何通过命令行覆盖配置文件`yolov3_mobilenet_v1_voc.yml`中的参数。 + +### 保存断点(checkpoint) + +如果在配置文件中设置了`checkpoint_path`, 则在压缩任务执行过程中会自动保存断点,当任务异常中断时, +重启任务会自动从`checkpoint_path`路径下按数字顺序加载最新的checkpoint文件。如果不想让重启的任务从断点恢复, +需要修改配置文件中的`checkpoint_path`,或者将`checkpoint_path`路径下文件清空。 + +>注意:配置文件中的信息不会保存在断点中,重启前对配置文件的修改将会生效。 + + +## 评估 + +如果在配置文件中设置了`checkpoint_path`,则每个epoch会保存一个压缩后的用于评估的模型, +该模型会保存在`${checkpoint_path}/${epoch_id}/eval_model/`路径下,包含`__model__`和`__params__`两个文件。 +其中,`__model__`用于保存模型结构信息,`__params__`用于保存参数(parameters)信息。 + +如果不需要保存评估模型,可以在定义Compressor对象时,将`save_eval_model`选项设置为False(默认为True)。 + +运行命令为: +``` +python ../eval.py \ + --model_path ${checkpoint_path}/${epoch_id}/eval_model/ \ + --model_name __model__ \ + --params_name __params__ \ + -c ../../configs/yolov3_mobilenet_v1_voc.yml \ + -d "../../dataset/voc" +``` + +## 预测 + +如果在配置文件中设置了`checkpoint_path`,并且在定义Compressor对象时指定了`prune_infer_model`选项,则每个epoch都会 +保存一个`inference model`。该模型是通过删除eval_program中多余的operators而得到的。 + +该模型会保存在`${checkpoint_path}/${epoch_id}/eval_model/`路径下,包含`__model__.infer`和`__params__`两个文件。 +其中,`__model__.infer`用于保存模型结构信息,`__params__`用于保存参数(parameters)信息。 + +更多关于`prune_infer_model`选项的介绍,请参考:[Compressor介绍](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/usage.md#121-%E5%A6%82%E4%BD%95%E6%94%B9%E5%86%99%E6%99%AE%E9%80%9A%E8%AE%AD%E7%BB%83%E8%84%9A%E6%9C%AC) + +### python预测 + +在脚本PaddleDetection/tools/infer.py中展示了如何使用fluid python API加载使用预测模型进行预测。 + +运行命令为: +``` +python ../infer.py \ + --model_path ${checkpoint_path}/${epoch_id}/eval_model/ \ + --model_name __model__.infer \ + --params_name __params__ \ + -c ../../configs/yolov3_mobilenet_v1_voc.yml \ + --infer_dir ../../demo +``` + +### PaddleLite + +该示例中产出的预测(inference)模型可以直接用PaddleLite进行加载使用。 +关于PaddleLite如何使用,请参考:[PaddleLite使用文档](https://github.com/PaddlePaddle/Paddle-Lite/wiki#%E4%BD%BF%E7%94%A8) + +## 示例结果 + +> 当前release的结果并非超参调优后的最好结果,仅做示例参考,后续我们会优化当前结果。 + +### MobileNetV1-YOLO-V3 + +| FLOPS |Box AP| model_size |Paddle Fluid inference time(ms)| Paddle Lite inference time(ms)| +|---|---|---|---|---| +|baseline|76.2 |93M |- |-| +|-50%|69.48 |51M |- |-| + +## FAQ diff --git a/slim/prune/compress.py b/slim/prune/compress.py new file mode 100644 index 0000000000000000000000000000000000000000..66f9a0cb50514bfbdf2964024064caf6702cfbc4 --- /dev/null +++ b/slim/prune/compress.py @@ -0,0 +1,255 @@ +# 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 time +import multiprocessing +import numpy as np +import sys +sys.path.append("../../") +from paddle.fluid.contrib.slim import Compressor + + +def set_paddle_flags(**kwargs): + for key, value in kwargs.items(): + if os.environ.get(key, None) is None: + os.environ[key] = str(value) + + +# NOTE(paddle-dev): All of these flags should be set before +# `import paddle`. Otherwise, it would not take any effect. +set_paddle_flags( + FLAGS_eager_delete_tensor_gb=0, # enable GC to save memory +) + +from paddle import fluid +from ppdet.core.workspace import load_config, merge_config, create +from ppdet.data.data_feed import create_reader +from ppdet.utils.eval_utils import parse_fetches, eval_results +from ppdet.utils.cli import ArgsParser +from ppdet.utils.check import check_gpu +import ppdet.utils.checkpoint as checkpoint +from ppdet.modeling.model_input import create_feed + +import logging +FORMAT = '%(asctime)s-%(levelname)s: %(message)s' +logging.basicConfig(level=logging.INFO, format=FORMAT) +logger = logging.getLogger(__name__) + + +def eval_run(exe, compile_program, reader, keys, values, cls, test_feed): + """ + Run evaluation program, return program outputs. + """ + iter_id = 0 + results = [] + if len(cls) != 0: + values = [] + for i in range(len(cls)): + _, accum_map = cls[i].get_map_var() + cls[i].reset(exe) + values.append(accum_map) + + images_num = 0 + start_time = time.time() + has_bbox = 'bbox' in keys + for data in reader(): + data = test_feed.feed(data) + feed_data = {'image': data['image'], 'im_size': data['im_size']} + outs = exe.run(compile_program, + feed=feed_data, + fetch_list=[values[0]], + return_numpy=False) + outs.append(data['gt_box']) + outs.append(data['gt_label']) + outs.append(data['is_difficult']) + res = { + k: (np.array(v), v.recursive_sequence_lengths()) + for k, v in zip(keys, outs) + } + results.append(res) + if iter_id % 100 == 0: + logger.info('Test iter {}'.format(iter_id)) + iter_id += 1 + images_num += len(res['bbox'][1][0]) if has_bbox else 1 + logger.info('Test finish iter {}'.format(iter_id)) + + end_time = time.time() + fps = images_num / (end_time - start_time) + if has_bbox: + logger.info('Total number of images: {}, inference time: {} fps.'. + format(images_num, fps)) + else: + logger.info('Total iteration: {}, inference time: {} batch/s.'.format( + images_num, fps)) + + return results + + +def main(): + cfg = load_config(FLAGS.config) + if 'architecture' in cfg: + main_arch = cfg.architecture + else: + raise ValueError("'architecture' not specified in config file.") + + merge_config(FLAGS.opt) + if 'log_iter' not in cfg: + cfg.log_iter = 20 + + # check if set use_gpu=True in paddlepaddle cpu version + check_gpu(cfg.use_gpu) + + if cfg.use_gpu: + devices_num = fluid.core.get_cuda_device_count() + else: + devices_num = int( + os.environ.get('CPU_NUM', multiprocessing.cpu_count())) + + if 'train_feed' not in cfg: + train_feed = create(main_arch + 'TrainFeed') + else: + train_feed = create(cfg.train_feed) + + if 'eval_feed' not in cfg: + eval_feed = create(main_arch + 'EvalFeed') + else: + eval_feed = create(cfg.eval_feed) + + place = fluid.CUDAPlace(0) if cfg.use_gpu else fluid.CPUPlace() + exe = fluid.Executor(place) + + lr_builder = create('LearningRate') + optim_builder = create('OptimizerBuilder') + + # build program + startup_prog = fluid.Program() + train_prog = fluid.Program() + with fluid.program_guard(train_prog, startup_prog): + with fluid.unique_name.guard(): + model = create(main_arch) + _, feed_vars = create_feed(train_feed, False) + train_fetches = model.train(feed_vars) + loss = train_fetches['loss'] + lr = lr_builder() + optimizer = optim_builder(lr) + optimizer.minimize(loss) + + train_reader = create_reader(train_feed, cfg.max_iters, FLAGS.dataset_dir) + + # parse train fetches + train_keys, train_values, _ = parse_fetches(train_fetches) + train_keys.append("lr") + train_values.append(lr.name) + + train_fetch_list = [] + for k, v in zip(train_keys, train_values): + train_fetch_list.append((k, v)) + + eval_prog = fluid.Program() + with fluid.program_guard(eval_prog, startup_prog): + with fluid.unique_name.guard(): + model = create(main_arch) + _, test_feed_vars = create_feed(eval_feed, False) + fetches = model.eval(test_feed_vars) + + eval_prog = eval_prog.clone(True) + + eval_reader = create_reader(eval_feed, args_path=FLAGS.dataset_dir) + test_data_feed = fluid.DataFeeder(test_feed_vars.values(), place) + + # parse eval fetches + 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'] + eval_keys, eval_values, eval_cls = parse_fetches(fetches, eval_prog, + extra_keys) + eval_fetch_list = [] + for k, v in zip(eval_keys, eval_values): + eval_fetch_list.append((k, v)) + + exe.run(startup_prog) + checkpoint.load_params(exe, train_prog, cfg.pretrain_weights) + + best_box_ap_list = [] + + def eval_func(program, scope): + + #place = fluid.CPUPlace() + #exe = fluid.Executor(place) + results = eval_run(exe, program, eval_reader, eval_keys, eval_values, + eval_cls, test_data_feed) + + resolution = None + if 'mask' in results[0]: + resolution = model.mask_head.resolution + box_ap_stats = eval_results(results, eval_feed, cfg.metric, + cfg.num_classes, resolution, False, + FLAGS.output_eval) + if len(best_box_ap_list) == 0: + best_box_ap_list.append(box_ap_stats[0]) + elif box_ap_stats[0] > best_box_ap_list[0]: + best_box_ap_list[0] = box_ap_stats[0] + logger.info("Best test box ap: {}".format(best_box_ap_list[0])) + return best_box_ap_list[0] + + test_feed = [('image', test_feed_vars['image'].name), + ('im_size', test_feed_vars['im_size'].name)] + + com = Compressor( + place, + fluid.global_scope(), + train_prog, + train_reader=train_reader, + train_feed_list=[(key, value.name) for key, value in feed_vars.items()], + train_fetch_list=train_fetch_list, + eval_program=eval_prog, + eval_reader=eval_reader, + eval_feed_list=test_feed, + eval_func={'map': eval_func}, + eval_fetch_list=[eval_fetch_list[0]], + save_eval_model=True, + prune_infer_model=[["image", "im_size"], ["multiclass_nms_0.tmp_0"]], + train_optimizer=None) + com.config(FLAGS.slim_file) + com.run() + + +if __name__ == '__main__': + parser = ArgsParser() + parser.add_argument( + "-s", + "--slim_file", + default=None, + type=str, + help="Config file of PaddleSlim.") + parser.add_argument( + "--output_eval", + 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") + FLAGS = parser.parse_args() + main() diff --git a/slim/prune/images/MobileNetV1-YoloV3.pdf b/slim/prune/images/MobileNetV1-YoloV3.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f5d3a22db5030ffc6beb1e7f8c92bddd61e366e6 Binary files /dev/null and b/slim/prune/images/MobileNetV1-YoloV3.pdf differ diff --git a/slim/prune/yolov3_mobilenet_v1_slim.yaml b/slim/prune/yolov3_mobilenet_v1_slim.yaml new file mode 100644 index 0000000000000000000000000000000000000000..ff9859eafe5872dee13f33a3703502e334d1c85e --- /dev/null +++ b/slim/prune/yolov3_mobilenet_v1_slim.yaml @@ -0,0 +1,23 @@ +version: 1.0 +pruners: + pruner_1: + class: 'StructurePruner' + pruning_axis: + '*': 0 + criterions: + '*': 'l1_norm' +strategies: + uniform_pruning_strategy: + class: 'UniformPruneStrategy' + pruner: 'pruner_1' + start_epoch: 0 + target_ratio: 0.5 + pruned_params: '(conv2_1_sep_weights)|(conv2_2_sep_weights)|(conv3_1_sep_weights)|(conv4_1_sep_weights)|(conv5_1_sep_weights)|(conv5_2_sep_weights)|(conv5_3_sep_weights)|(conv5_4_sep_weights)|(conv5_5_sep_weights)|(conv5_6_sep_weights)|(yolo_block.0.0.0.conv.weights)|(yolo_block.0.0.1.conv.weights)|(yolo_block.0.1.0.conv.weights)|(yolo_block.0.1.1.conv.weights)|(yolo_block.1.0.0.conv.weights)|(yolo_block.1.0.1.conv.weights)|(yolo_block.1.1.0.conv.weights)|(yolo_block.1.1.1.conv.weights)|(yolo_block.1.2.conv.weights)|(yolo_block.2.0.0.conv.weights)|(yolo_block.2.0.1.conv.weights)|(yolo_block.2.1.1.conv.weights)|(yolo_block.2.2.conv.weights)|(yolo_block.2.tip.conv.weights)' + metric_name: 'acc_top1' +compressor: + epoch: 271 + eval_epoch: 10 + #init_model: './checkpoints/0' # Please enable this option for loading checkpoint. + checkpoint_path: './checkpoints/' + strategies: + - uniform_pruning_strategy diff --git a/slim/quantization/README.md b/slim/quantization/README.md new file mode 100644 index 0000000000000000000000000000000000000000..acb4c9efcbd49bccc4682c7eb7af294885e5d42a --- /dev/null +++ b/slim/quantization/README.md @@ -0,0 +1,241 @@ +>运行该示例前请安装Paddle1.6或更高版本 + +# 检测模型量化压缩示例 + +## 概述 + +该示例使用PaddleSlim提供的[量化压缩策略](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/tutorial.md#1-quantization-aware-training%E9%87%8F%E5%8C%96%E4%BB%8B%E7%BB%8D)对分类模型进行压缩。 +在阅读该示例前,建议您先了解以下内容: + +- [检测模型的常规训练方法](https://github.com/PaddlePaddle/models/tree/develop/PaddleCV/PaddleDetection) +- [PaddleSlim使用文档](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/usage.md) + + +## 配置文件说明 + +关于配置文件如何编写您可以参考: + +- [PaddleSlim配置文件编写说明](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/usage.md#122-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E7%9A%84%E4%BD%BF%E7%94%A8) +- [量化策略配置文件编写说明](https://github.com/PaddlePaddle/models/blob/develop/PaddleSlim/docs/usage.md#21-%E9%87%8F%E5%8C%96%E8%AE%AD%E7%BB%83) + +其中save_out_nodes需要得到检测结果的Variable的名称,下面介绍如何确定save_out_nodes的参数 +以MobileNet V1为例,可在compress.py中构建好网络之后,直接打印Variable得到Variable的名称信息。 +代码示例: +``` + eval_keys, eval_values, eval_cls = parse_fetches(fetches, eval_prog, + extra_keys) + # print(eval_values) +``` +根据运行结果可看到Variable的名字为:`multiclass_nms_0.tmp_0`。 +## 训练 + +根据 [PaddleCV/PaddleDetection/tools/train.py](https://github.com/PaddlePaddle/models/blob/develop/PaddleCV/PaddleDetection/tools/train.py) 编写压缩脚本compress.py。 +在该脚本中定义了Compressor对象,用于执行压缩任务。 + +通过`python compress.py --help`查看可配置参数,简述如下: + +- config: 检测库的配置,其中配置了训练超参数、数据集信息等。 +- slim_file: PaddleSlim的配置文件,参见[配置文件说明](#配置文件说明)。 + +您可以通过运行以下命令运行该示例。 + +step1: 设置gpu卡 +``` +export CUDA_VISIBLE_DEVICES=0 +``` +step2: 开始训练 +使用PaddleDetection提供的配置文件在用8卡进行训练: + +``` +python compress.py \ + -s yolov3_mobilenet_v1_slim.yaml \ + -c ../../configs/yolov3_mobilenet_v1_voc.yml \ + -d "../../dataset/voc" \ + -o max_iters=258 \ + LearningRate.base_lr=0.0001 \ + LearningRate.schedulers="[!PiecewiseDecay {gamma: 0.1, milestones: [258, 516]}]" \ + pretrain_weights=https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1_voc.tar \ + YoloTrainFeed.batch_size=64 +``` + +>通过命令行覆盖设置max_iters选项,因为PaddleDetection中训练是以`batch`为单位迭代的,并没有涉及`epoch`的概念,但是PaddleSlim需要知道当前训练进行到第几个`epoch`, 所以需要将`max_iters`设置为一个`epoch`内的`batch`的数量。 + +如果要调整训练卡数,需要调整配置文件`yolov3_mobilenet_v1_voc.yml`中的以下参数: + +- **max_iters:** 一个`epoch`中batch的数量,需要设置为`total_num / batch_size`, 其中`total_num`为训练样本总数量,`batch_size`为多卡上总的batch size. +- **YoloTrainFeed.batch_size:** 当使用DataLoader时,表示单张卡上的batch size; 当使用普通reader时,则表示多卡上的总的batch_size。batch_size受限于显存大小。 +- **LeaningRate.base_lr:** 根据多卡的总`batch_size`调整`base_lr`,两者大小正相关,可以简单的按比例进行调整。 +- **LearningRate.schedulers.PiecewiseDecay.milestones:** 请根据batch size的变化对其调整。 +- **LearningRate.schedulers.PiecewiseDecay.LinearWarmup.steps:** 请根据batch size的变化对其进行调整。 + + +以下为4卡训练示例,通过命令行覆盖`yolov3_mobilenet_v1_voc.yml`中的参数: + +``` +python compress.py \ + -s yolov3_mobilenet_v1_slim.yaml \ + -c ../../configs/yolov3_mobilenet_v1_voc.yml \ + -d "../../dataset/voc" \ + -o max_iters=258 \ + LearningRate.base_lr=0.0001 \ + LearningRate.schedulers="[!PiecewiseDecay {gamma: 0.1, milestones: [258, 516]}]" \ + pretrain_weights=https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1_voc.tar \ + YoloTrainFeed.batch_size=64 + +``` + +以下为2卡训练示例,受显存所制,单卡`batch_size`不变, 总`batch_size`减小,`base_lr`减小,一个epoch内batch数量增加,同时需要调整学习率相关参数,如下: + +``` +python compress.py \ + -s yolov3_mobilenet_v1_slim.yaml \ + -c ../../configs/yolov3_mobilenet_v1_voc.yml \ + -d "../../dataset/voc" \ + -o max_iters=516 \ + LearningRate.base_lr=0.00005 \ + LearningRate.schedulers="[!PiecewiseDecay {gamma: 0.1, milestones: [516, 1012]}]" \ + pretrain_weights=https://paddlemodels.bj.bcebos.com/object_detection/yolov3_mobilenet_v1_voc.tar \ + YoloTrainFeed.batch_size=32 +``` + +通过`python compress.py --help`查看可配置参数。 +通过`python ../../tools/configure.py ${option_name} help`查看如何通过命令行覆盖配置文件`yolov3_mobilenet_v1_voc.yml`中的参数。 + + + +### 训练时的模型结构 +这部分介绍来源于[量化low-level API介绍](https://github.com/PaddlePaddle/models/tree/develop/PaddleSlim/quant_low_level_api#1-%E9%87%8F%E5%8C%96%E8%AE%AD%E7%BB%83low-level-apis%E4%BB%8B%E7%BB%8D)。 + +PaddlePaddle框架中和量化相关的IrPass, 分别有QuantizationTransformPass、QuantizationFreezePass、ConvertToInt8Pass。在训练时,对网络应用了QuantizationTransformPass,作用是在网络中的conv2d、depthwise_conv2d、mul等算子的各个输入前插入连续的量化op和反量化op,并改变相应反向算子的某些输入。示例图如下: + +

+
+图1:应用QuantizationTransformPass后的结果 +

+ +### 保存断点(checkpoint) + +如果在配置文件中设置了`checkpoint_path`, 则在压缩任务执行过程中会自动保存断点,当任务异常中断时, +重启任务会自动从`checkpoint_path`路径下按数字顺序加载最新的checkpoint文件。如果不想让重启的任务从断点恢复, +需要修改配置文件中的`checkpoint_path`,或者将`checkpoint_path`路径下文件清空。 + +>注意:配置文件中的信息不会保存在断点中,重启前对配置文件的修改将会生效。 + + +### 保存评估和预测模型 + +如果在配置文件的量化策略中设置了`float_model_save_path`, `int8_model_save_path` 在训练结束后,会保存模型量化压缩之后用于预测的模型。接下来介绍这2种预测模型的区别。 + +#### FP32模型 +在介绍量化训练时的模型结构时介绍了PaddlePaddle框架中和量化相关的IrPass, 分别是QuantizationTransformPass、QuantizationFreezePass、ConvertToInt8Pass。FP32模型是在应用QuantizationFreezePass并删除eval_program中多余的operators之后,保存的模型。 + +QuantizationFreezePass主要用于改变IrGraph中量化op和反量化op的顺序,即将类似图1中的量化op和反量化op顺序改变为图2中的布局。除此之外,QuantizationFreezePass还会将`conv2d`、`depthwise_conv2d`、`mul`等算子的权重离线量化为int8_t范围内的值(但数据类型仍为float32),以减少预测过程中对权重的量化操作,示例如图2: + +

+
+图2:应用QuantizationFreezePass后的结果 +

+ +#### 8-bit模型 +在对训练网络进行QuantizationFreezePass之后,执行ConvertToInt8Pass, +其主要目的是将执行完QuantizationFreezePass后输出的权重类型由`FP32`更改为`INT8`。换言之,用户可以选择将量化后的权重保存为float32类型(不执行ConvertToInt8Pass)或者int8_t类型(执行ConvertToInt8Pass),示例如图3: + +

+
+图3:应用ConvertToInt8Pass后的结果 +

+ +> 综上,可得在量化过程中有以下几种模型结构: + +1. 原始模型 +2. 经QuantizationTransformPass之后得到的适用于训练的量化模型结构,在${checkpoint_path}下保存的`eval_model`是这种结构,在训练过程中每个epoch结束时也使用这个网络结构进行评估,虽然这个模型结构不是最终想要的模型结构,但是每个epoch的评估结果可用来挑选模型。 +3. 经QuantizationFreezePass之后得到的FP32模型结构,具体结构已在上面进行介绍。本文档中列出的数据集的评估结果是对FP32模型结构进行评估得到的结果。这种模型结构在训练过程中只会保存一次,也就是在量化配置文件中设置的`end_epoch`结束时进行保存,如果想将其他epoch的训练结果转化成FP32模型,可使用脚本 PaddleSlim/classification/quantization/freeze.py进行转化,具体使用方法在[评估](#评估)中介绍。 +4. 经ConvertToInt8Pass之后得到的8-bit模型结构,具体结构已在上面进行介绍。这种模型结构在训练过程中只会保存一次,也就是在量化配置文件中设置的`end_epoch`结束时进行保存,如果想将其他epoch的训练结果转化成8-bit模型,可使用脚本 slim/quantization/freeze.py进行转化,具体使用方法在[评估](#评估)中介绍。 + + +## 评估 + +### 每个epoch保存的评估模型 +因为量化的最终模型只有在end_epoch时保存一次,不能保证保存的模型是最好的,因此 +如果在配置文件中设置了`checkpoint_path`,则每个epoch会保存一个量化后的用于评估的模型, +该模型会保存在`${checkpoint_path}/${epoch_id}/eval_model/`路径下,包含`__model__`和`__params__`两个文件。 +其中,`__model__`用于保存模型结构信息,`__params__`用于保存参数(parameters)信息。模型结构和训练时一样。 + +如果不需要保存评估模型,可以在定义Compressor对象时,将`save_eval_model`选项设置为False(默认为True)。 + +脚本slim/eval.py中为使用该模型在评估数据集上做评估的示例。 +运行命令为: +``` +python ../eval.py \ + --model_path ${checkpoint_path}/${epoch_id}/eval_model/ \ + --model_name __model__ \ + --params_name __params__ \ + -c ../../configs/yolov3_mobilenet_v1_voc.yml \ + -d "../../dataset/voc" +``` + +在评估之后,选取效果最好的epoch的模型,可使用脚本 slim/quantization/freeze.py将该模型转化为以上介绍的2种模型:FP32模型,int8模型,需要配置的参数为: + +- model_path, 加载的模型路径,`为${checkpoint_path}/${epoch_id}/eval_model/` +- weight_quant_type 模型参数的量化方式,和配置文件中的类型保持一致 +- save_path `FP32`, `8-bit` 模型的保存路径,分别为 `${save_path}/float/`, `${save_path}/int8/` + +运行命令示例: +``` +python freeze.py \ + --model_path ${checkpoint_path}/${epoch_id}/eval_model/ \ + --weight_quant_type ${weight_quant_type} \ + --save_path ${any path you want} \ + -c ../../configs/yolov3_mobilenet_v1_voc.yml \ + -d "../../dataset/voc" +``` + +### 最终评估模型 +最终使用的评估模型是FP32模型,使用脚本slim/eval.py中为使用该模型在评估数据集上做评估的示例。 +运行命令为: +``` +python ../eval.py \ + --model_path ${float_model_path} + --model_name model \ + --params_name weights \ + -c ../../configs/yolov3_mobilenet_v1_voc.yml \ + -d "../../dataset/voc" +``` + +## 预测 + +### python预测 +FP32模型可直接使用原生PaddlePaddle Fluid预测方法进行预测。 + +在脚本slim/infer.py中展示了如何使用fluid python API加载使用预测模型进行预测。 + +运行命令示例: +``` +python ../infer.py \ + --model_path ${save_path}/float \ + --model_name model \ + --params_name weights \ + -c ../../configs/yolov3_mobilenet_v1_voc.yml \ + --infer_dir ../../demo +``` + + +### PaddleLite预测 +FP32模型可使用PaddleLite进行加载预测,可参见教程[Paddle-Lite如何加载运行量化模型](https://github.com/PaddlePaddle/Paddle-Lite/wiki/model_quantization) + + +## 示例结果 + +>当前release的结果并非超参调优后的最好结果,仅做示例参考,后续我们会优化当前结果。 + +### MobileNetV1-YOLO-V3 + +| weight量化方式 | activation量化方式| Box ap |Paddle Fluid inference time(ms)| Paddle Lite inference time(ms)| +|---|---|---|---|---| +|baseline|- |76.2%|- |-| +|abs_max|abs_max|- |- |-| +|abs_max|moving_average_abs_max|- |- |-| +|channel_wise_abs_max|abs_max|- |- |-| + + +## FAQ diff --git a/slim/quantization/compress.py b/slim/quantization/compress.py new file mode 100644 index 0000000000000000000000000000000000000000..b4a5553cf46eabcd25f7cc1ce6c50fccefd2e5df --- /dev/null +++ b/slim/quantization/compress.py @@ -0,0 +1,267 @@ +# 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 time +import multiprocessing +import numpy as np +import datetime +from collections import deque +import sys +sys.path.append("../../") +from paddle.fluid.contrib.slim import Compressor +from paddle.fluid.framework import IrGraph +from paddle.fluid import core + + +def set_paddle_flags(**kwargs): + for key, value in kwargs.items(): + if os.environ.get(key, None) is None: + os.environ[key] = str(value) + + +# NOTE(paddle-dev): All of these flags should be set before +# `import paddle`. Otherwise, it would not take any effect. +set_paddle_flags( + FLAGS_eager_delete_tensor_gb=0, # enable GC to save memory +) + +from paddle import fluid + +from ppdet.core.workspace import load_config, merge_config, create +from ppdet.data.data_feed import create_reader + +from ppdet.utils.eval_utils import parse_fetches, eval_results +from ppdet.utils.stats import TrainingStats +from ppdet.utils.cli import ArgsParser, print_total_cfg +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' +logging.basicConfig(level=logging.INFO, format=FORMAT) +logger = logging.getLogger(__name__) + + +def eval_run(exe, compile_program, reader, keys, values, cls, test_feed): + """ + Run evaluation program, return program outputs. + """ + iter_id = 0 + results = [] + if len(cls) != 0: + values = [] + for i in range(len(cls)): + _, accum_map = cls[i].get_map_var() + cls[i].reset(exe) + values.append(accum_map) + + images_num = 0 + start_time = time.time() + has_bbox = 'bbox' in keys + for data in reader(): + data = test_feed.feed(data) + feed_data = {'image': data['image'], 'im_size': data['im_size']} + outs = exe.run(compile_program, + feed=feed_data, + fetch_list=[values[0]], + return_numpy=False) + outs.append(data['gt_box']) + outs.append(data['gt_label']) + outs.append(data['is_difficult']) + res = { + k: (np.array(v), v.recursive_sequence_lengths()) + for k, v in zip(keys, outs) + } + results.append(res) + if iter_id % 100 == 0: + logger.info('Test iter {}'.format(iter_id)) + iter_id += 1 + images_num += len(res['bbox'][1][0]) if has_bbox else 1 + logger.info('Test finish iter {}'.format(iter_id)) + + end_time = time.time() + fps = images_num / (end_time - start_time) + if has_bbox: + logger.info('Total number of images: {}, inference time: {} fps.'. + format(images_num, fps)) + else: + logger.info('Total iteration: {}, inference time: {} batch/s.'.format( + images_num, fps)) + + return results + + +def main(): + cfg = load_config(FLAGS.config) + if 'architecture' in cfg: + main_arch = cfg.architecture + else: + raise ValueError("'architecture' not specified in config file.") + + merge_config(FLAGS.opt) + if 'log_iter' not in cfg: + cfg.log_iter = 20 + + # check if set use_gpu=True in paddlepaddle cpu version + check_gpu(cfg.use_gpu) + # print_total_cfg(cfg) + #check_version() + if cfg.use_gpu: + devices_num = fluid.core.get_cuda_device_count() + else: + devices_num = int( + os.environ.get('CPU_NUM', multiprocessing.cpu_count())) + + if 'train_feed' not in cfg: + train_feed = create(main_arch + 'TrainFeed') + else: + train_feed = create(cfg.train_feed) + + if 'eval_feed' not in cfg: + eval_feed = create(main_arch + 'EvalFeed') + else: + eval_feed = create(cfg.eval_feed) + + place = fluid.CUDAPlace(0) if cfg.use_gpu else fluid.CPUPlace() + exe = fluid.Executor(place) + + lr_builder = create('LearningRate') + optim_builder = create('OptimizerBuilder') + + # build program + startup_prog = fluid.Program() + train_prog = fluid.Program() + with fluid.program_guard(train_prog, startup_prog): + with fluid.unique_name.guard(): + model = create(main_arch) + _, feed_vars = create_feed(train_feed, False) + train_fetches = model.train(feed_vars) + loss = train_fetches['loss'] + lr = lr_builder() + optimizer = optim_builder(lr) + optimizer.minimize(loss) + + train_reader = create_reader(train_feed, cfg.max_iters, FLAGS.dataset_dir) + + # parse train fetches + train_keys, train_values, _ = parse_fetches(train_fetches) + train_values.append(lr) + + train_fetch_list = [] + for k, v in zip(train_keys, train_values): + train_fetch_list.append((k, v)) + print("train_fetch_list: {}".format(train_fetch_list)) + + eval_prog = fluid.Program() + with fluid.program_guard(eval_prog, startup_prog): + with fluid.unique_name.guard(): + model = create(main_arch) + _, test_feed_vars = create_feed(eval_feed, False) + fetches = model.eval(test_feed_vars) + eval_prog = eval_prog.clone(True) + + eval_reader = create_reader(eval_feed, args_path=FLAGS.dataset_dir) + #eval_pyreader.decorate_sample_list_generator(eval_reader, place) + test_data_feed = fluid.DataFeeder(test_feed_vars.values(), place) + + # parse eval fetches + 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'] + eval_keys, eval_values, eval_cls = parse_fetches(fetches, eval_prog, + extra_keys) + # print(eval_values) + + eval_fetch_list = [] + for k, v in zip(eval_keys, eval_values): + eval_fetch_list.append((k, v)) + + exe.run(startup_prog) + + start_iter = 0 + + checkpoint.load_params(exe, train_prog, cfg.pretrain_weights) + + best_box_ap_list = [] + + def eval_func(program, scope): + + #place = fluid.CPUPlace() + #exe = fluid.Executor(place) + results = eval_run(exe, program, eval_reader, eval_keys, eval_values, + eval_cls, test_data_feed) + + resolution = None + if 'mask' in results[0]: + resolution = model.mask_head.resolution + box_ap_stats = eval_results(results, eval_feed, cfg.metric, + cfg.num_classes, resolution, False, + FLAGS.output_eval) + if len(best_box_ap_list) == 0: + best_box_ap_list.append(box_ap_stats[0]) + elif box_ap_stats[0] > best_box_ap_list[0]: + best_box_ap_list[0] = box_ap_stats[0] + logger.info("Best test box ap: {}".format(best_box_ap_list[0])) + return best_box_ap_list[0] + + test_feed = [('image', test_feed_vars['image'].name), + ('im_size', test_feed_vars['im_size'].name)] + + com = Compressor( + place, + fluid.global_scope(), + train_prog, + train_reader=train_reader, + train_feed_list=[(key, value.name) for key, value in feed_vars.items()], + train_fetch_list=train_fetch_list, + eval_program=eval_prog, + eval_reader=eval_reader, + eval_feed_list=test_feed, + eval_func={'map': eval_func}, + eval_fetch_list=[eval_fetch_list[0]], + prune_infer_model=[["image", "im_size"], ["multiclass_nms_0.tmp_0"]], + train_optimizer=None) + com.config(FLAGS.slim_file) + com.run() + + +if __name__ == '__main__': + parser = ArgsParser() + parser.add_argument( + "-s", + "--slim_file", + default=None, + type=str, + help="Config file of PaddleSlim.") + parser.add_argument( + "--output_eval", + 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") + FLAGS = parser.parse_args() + main() diff --git a/slim/quantization/freeze.py b/slim/quantization/freeze.py new file mode 100644 index 0000000000000000000000000000000000000000..38c06578e3d22e1cc4f2bdcc933298553c1c1f37 --- /dev/null +++ b/slim/quantization/freeze.py @@ -0,0 +1,239 @@ +# 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 time +import multiprocessing +import numpy as np +import datetime +from collections import deque +import sys +sys.path.append("../../") +from paddle.fluid.contrib.slim import Compressor +from paddle.fluid.framework import IrGraph +from paddle.fluid import core +from paddle.fluid.contrib.slim.quantization import QuantizationTransformPass +from paddle.fluid.contrib.slim.quantization import QuantizationFreezePass +from paddle.fluid.contrib.slim.quantization import ConvertToInt8Pass +from paddle.fluid.contrib.slim.quantization import TransformForMobilePass + + +def set_paddle_flags(**kwargs): + for key, value in kwargs.items(): + if os.environ.get(key, None) is None: + os.environ[key] = str(value) + + +# NOTE(paddle-dev): All of these flags should be set before +# `import paddle`. Otherwise, it would not take any effect. +set_paddle_flags( + FLAGS_eager_delete_tensor_gb=0, # enable GC to save memory +) + +from paddle import fluid + +from ppdet.core.workspace import load_config, merge_config, create +from ppdet.data.data_feed import create_reader + +from ppdet.utils.eval_utils import parse_fetches, eval_results +from ppdet.utils.stats import TrainingStats +from ppdet.utils.cli import ArgsParser +from ppdet.utils.check import check_gpu +import ppdet.utils.checkpoint as checkpoint +from ppdet.modeling.model_input import create_feed + +import logging +FORMAT = '%(asctime)s-%(levelname)s: %(message)s' +logging.basicConfig(level=logging.INFO, format=FORMAT) +logger = logging.getLogger(__name__) + + +def eval_run(exe, compile_program, reader, keys, values, cls, test_feed): + """ + Run evaluation program, return program outputs. + """ + iter_id = 0 + results = [] + + images_num = 0 + start_time = time.time() + has_bbox = 'bbox' in keys + for data in reader(): + data = test_feed.feed(data) + feed_data = {'image': data['image'], 'im_size': data['im_size']} + outs = exe.run(compile_program, + feed=feed_data, + fetch_list=values[0], + return_numpy=False) + outs.append(data['gt_box']) + outs.append(data['gt_label']) + outs.append(data['is_difficult']) + res = { + k: (np.array(v), v.recursive_sequence_lengths()) + for k, v in zip(keys, outs) + } + results.append(res) + if iter_id % 100 == 0: + logger.info('Test iter {}'.format(iter_id)) + iter_id += 1 + images_num += len(res['bbox'][1][0]) if has_bbox else 1 + logger.info('Test finish iter {}'.format(iter_id)) + + end_time = time.time() + fps = images_num / (end_time - start_time) + if has_bbox: + logger.info('Total number of images: {}, inference time: {} fps.'. + format(images_num, fps)) + else: + logger.info('Total iteration: {}, inference time: {} batch/s.'.format( + images_num, fps)) + + return results + + +def main(): + cfg = load_config(FLAGS.config) + if 'architecture' in cfg: + main_arch = cfg.architecture + else: + raise ValueError("'architecture' not specified in config file.") + + merge_config(FLAGS.opt) + if 'log_iter' not in cfg: + cfg.log_iter = 20 + + # check if set use_gpu=True in paddlepaddle cpu version + check_gpu(cfg.use_gpu) + + if cfg.use_gpu: + devices_num = fluid.core.get_cuda_device_count() + else: + devices_num = int( + os.environ.get('CPU_NUM', multiprocessing.cpu_count())) + + if 'eval_feed' not in cfg: + eval_feed = create(main_arch + 'EvalFeed') + else: + eval_feed = create(cfg.eval_feed) + + place = fluid.CUDAPlace(0) if cfg.use_gpu else fluid.CPUPlace() + exe = fluid.Executor(place) + + _, test_feed_vars = create_feed(eval_feed, False) + + eval_reader = create_reader(eval_feed, args_path=FLAGS.dataset_dir) + #eval_pyreader.decorate_sample_list_generator(eval_reader, place) + test_data_feed = fluid.DataFeeder(test_feed_vars.values(), place) + + assert os.path.exists(FLAGS.model_path) + infer_prog, feed_names, fetch_targets = fluid.io.load_inference_model( + dirname=FLAGS.model_path, + executor=exe, + model_filename='__model__.infer', + params_filename='__params__') + + eval_keys = ['bbox', 'gt_box', 'gt_label', 'is_difficult'] + eval_values = [ + 'multiclass_nms_0.tmp_0', 'gt_box', 'gt_label', 'is_difficult' + ] + eval_cls = [] + eval_values[0] = fetch_targets[0] + + results = eval_run(exe, infer_prog, eval_reader, eval_keys, eval_values, + eval_cls, test_data_feed) + + resolution = None + if 'mask' in results[0]: + resolution = model.mask_head.resolution + box_ap_stats = eval_results(results, eval_feed, cfg.metric, cfg.num_classes, + resolution, False, FLAGS.output_eval) + + logger.info("freeze the graph for inference") + test_graph = IrGraph(core.Graph(infer_prog.desc), for_test=True) + + freeze_pass = QuantizationFreezePass( + scope=fluid.global_scope(), + place=place, + weight_quantize_type=FLAGS.weight_quant_type) + freeze_pass.apply(test_graph) + server_program = test_graph.to_program() + fluid.io.save_inference_model( + dirname=os.path.join(FLAGS.save_path, 'float'), + feeded_var_names=feed_names, + target_vars=fetch_targets, + executor=exe, + main_program=server_program, + model_filename='model', + params_filename='weights') + + logger.info("convert the weights into int8 type") + convert_int8_pass = ConvertToInt8Pass( + scope=fluid.global_scope(), place=place) + convert_int8_pass.apply(test_graph) + server_int8_program = test_graph.to_program() + fluid.io.save_inference_model( + dirname=os.path.join(FLAGS.save_path, 'int8'), + feeded_var_names=feed_names, + target_vars=fetch_targets, + executor=exe, + main_program=server_int8_program, + model_filename='model', + params_filename='weights') + + logger.info("convert the freezed pass to paddle-lite execution") + mobile_pass = TransformForMobilePass() + mobile_pass.apply(test_graph) + mobile_program = test_graph.to_program() + fluid.io.save_inference_model( + dirname=os.path.join(FLAGS.save_path, 'mobile'), + feeded_var_names=feed_names, + target_vars=fetch_targets, + executor=exe, + main_program=mobile_program, + model_filename='model', + params_filename='weights') + + +if __name__ == '__main__': + parser = ArgsParser() + parser.add_argument( + "-m", "--model_path", default=None, type=str, help="path of checkpoint") + parser.add_argument( + "--output_eval", + 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( + "--weight_quant_type", + default='abs_max', + type=str, + help="quantization type for weight") + parser.add_argument( + "--save_path", + default='./output', + type=str, + help="path to save quantization inference model") + + FLAGS = parser.parse_args() + main() diff --git a/slim/quantization/images/ConvertToInt8Pass.png b/slim/quantization/images/ConvertToInt8Pass.png new file mode 100644 index 0000000000000000000000000000000000000000..8b5849819c0bc8e592dc8f864d8945330df85ab1 Binary files /dev/null and b/slim/quantization/images/ConvertToInt8Pass.png differ diff --git a/slim/quantization/images/FreezePass.png b/slim/quantization/images/FreezePass.png new file mode 100644 index 0000000000000000000000000000000000000000..acd2b0a890a8af85bec6eecdb22e47ad386a178c Binary files /dev/null and b/slim/quantization/images/FreezePass.png differ diff --git a/slim/quantization/images/TransformForMobilePass.png b/slim/quantization/images/TransformForMobilePass.png new file mode 100644 index 0000000000000000000000000000000000000000..4104cacc67af0be1c7bc152696e2ae544127aace Binary files /dev/null and b/slim/quantization/images/TransformForMobilePass.png differ diff --git a/slim/quantization/images/TransformPass.png b/slim/quantization/images/TransformPass.png new file mode 100644 index 0000000000000000000000000000000000000000..f29ab62753e0e6ddf28d0c1dda7139705fc24b18 Binary files /dev/null and b/slim/quantization/images/TransformPass.png differ diff --git a/slim/quantization/yolov3_mobilenet_v1_slim.yaml b/slim/quantization/yolov3_mobilenet_v1_slim.yaml new file mode 100644 index 0000000000000000000000000000000000000000..60a66f656f9e419cd862231654ab4eaca6057ea2 --- /dev/null +++ b/slim/quantization/yolov3_mobilenet_v1_slim.yaml @@ -0,0 +1,20 @@ +version: 1.0 +strategies: + quantization_strategy: + class: 'QuantizationStrategy' + start_epoch: 0 + end_epoch: 4 + float_model_save_path: './output/yolov3/float' + mobile_model_save_path: './output/yolov3/mobile' + int8_model_save_path: './output/yolov3/int8' + weight_bits: 8 + activation_bits: 8 + weight_quantize_type: 'abs_max' + activation_quantize_type: 'moving_average_abs_max' + save_in_nodes: ['image', 'im_size'] + save_out_nodes: ['multiclass_nms_0.tmp_0'] +compressor: + epoch: 5 + checkpoint_path: './checkpoints/yolov3/' + strategies: + - quantization_strategy diff --git a/tools/__init__.py b/tools/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..d0c32e26092f6ea25771279418582a24ea449ab2 --- /dev/null +++ b/tools/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/tools/configure.py b/tools/configure.py new file mode 100644 index 0000000000000000000000000000000000000000..560d161513ae8f0115d8d3d5f97f6a0695642015 --- /dev/null +++ b/tools/configure.py @@ -0,0 +1,202 @@ +# 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 print_function + +import sys +from argparse import ArgumentParser, RawDescriptionHelpFormatter + +import yaml + +from ppdet.core.workspace import get_registered_modules, load_config, dump_value +from ppdet.utils.cli import ColorTTY, print_total_cfg + +color_tty = ColorTTY() + +MISC_CONFIG = { + "architecture": "", + "max_iters": "", + "train_feed": "", + "eval_feed": "", + "test_feed": "", + "pretrain_weights": "", + "save_dir": "", + "weights": "", + "metric": "", + "map_type": "11point", + "log_smooth_window": 20, + "snapshot_iter": 10000, + "log_iter": 20, + "use_gpu": True, + "finetune_exclude_pretrained_params": "", +} + + +def dump_config(module, minimal=False): + args = module.schema.values() + if minimal: + args = [arg for arg in args if not arg.has_default()] + return yaml.dump( + { + module.name: { + arg.name: arg.default if arg.has_default() else "" + for arg in args + } + }, + default_flow_style=False, + default_style='') + + +def list_modules(**kwargs): + target_category = kwargs['category'] + module_schema = get_registered_modules() + module_by_category = {} + + for schema in module_schema.values(): + category = schema.category + if target_category is not None and schema.category != target_category: + continue + if category not in module_by_category: + module_by_category[category] = [schema] + else: + module_by_category[category].append(schema) + + for cat, modules in module_by_category.items(): + print("Available modules in the category '{}':".format(cat)) + print("") + max_len = max([len(mod.name) for mod in modules]) + for mod in modules: + print(color_tty.green(mod.name.ljust(max_len)), + mod.doc.split('\n')[0]) + print("") + + +def help_module(**kwargs): + schema = get_registered_modules()[kwargs['module']] + + doc = schema.doc is None and "Not documented" or "{}".format(schema.doc) + func_args = {arg.name: arg.doc for arg in schema.schema.values()} + max_len = max([len(k) for k in func_args.keys()]) + opts = "\n".join([ + "{} {}".format(color_tty.green(k.ljust(max_len)), v) + for k, v in func_args.items() + ]) + template = dump_config(schema) + print("{}\n\n{}\n\n{}\n\n{}\n\n{}\n\n{}\n{}\n".format( + color_tty.bold(color_tty.blue("MODULE DESCRIPTION:")), + doc, + color_tty.bold(color_tty.blue("MODULE OPTIONS:")), + opts, + color_tty.bold(color_tty.blue("CONFIGURATION TEMPLATE:")), + template, + color_tty.bold(color_tty.blue("COMMAND LINE OPTIONS:")), )) + for arg in schema.schema.values(): + print("--opt {}.{}={}".format(schema.name, arg.name, + dump_value(arg.default) + if arg.has_default() else "")) + + +def generate_config(**kwargs): + minimal = kwargs['minimal'] + modules = kwargs['modules'] + module_schema = get_registered_modules() + visited = [] + schema = [] + + def walk(m): + if m in visited: + return + s = module_schema[m] + schema.append(s) + visited.append(m) + + for mod in modules: + walk(mod) + + # XXX try to be smart about when to add header, + # if any "architecture" module, is included, head will be added as well + if any([getattr(m, 'category', None) == 'architecture' for m in schema]): + # XXX for ordered printing + header = "" + for k, v in MISC_CONFIG.items(): + header += yaml.dump( + { + k: v + }, default_flow_style=False, default_style='') + print(header) + + for s in schema: + print(dump_config(s, minimal)) + + +# FIXME this is pretty hackish, maybe implement a custom YAML printer? +def analyze_config(**kwargs): + config = load_config(kwargs['file']) + print_total_cfg(config) + + +if __name__ == '__main__': + argv = sys.argv[1:] + + parser = ArgumentParser(formatter_class=RawDescriptionHelpFormatter) + subparsers = parser.add_subparsers(help='Supported Commands') + list_parser = subparsers.add_parser("list", help="list available modules") + help_parser = subparsers.add_parser( + "help", help="show detail options for module") + generate_parser = subparsers.add_parser( + "generate", help="generate configuration template") + analyze_parser = subparsers.add_parser( + "analyze", help="analyze configuration file") + + list_parser.set_defaults(func=list_modules) + help_parser.set_defaults(func=help_module) + generate_parser.set_defaults(func=generate_config) + analyze_parser.set_defaults(func=analyze_config) + + list_group = list_parser.add_mutually_exclusive_group() + list_group.add_argument( + "-c", + "--category", + type=str, + default=None, + help="list modules for ") + + help_parser.add_argument( + "module", + help="module to show info for", + choices=list(get_registered_modules().keys())) + + generate_parser.add_argument( + "modules", + nargs='+', + help="include these module in generated configuration template", + choices=list(get_registered_modules().keys())) + generate_group = generate_parser.add_mutually_exclusive_group() + generate_group.add_argument( + "--minimal", action='store_true', help="only include required options") + generate_group.add_argument( + "--full", + action='store_false', + dest='minimal', + help="include all options") + + analyze_parser.add_argument("file", help="configuration file to analyze") + + if len(sys.argv) < 2: + parser.print_help() + sys.exit(1) + + args = parser.parse_args(argv) + if hasattr(args, 'func'): + args.func(**vars(args)) diff --git a/tools/eval.py b/tools/eval.py new file mode 100644 index 0000000000000000000000000000000000000000..15692d3dd8b13789faaaa6a9a0bb3ed9385f37a6 --- /dev/null +++ b/tools/eval.py @@ -0,0 +1,183 @@ +# 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 + + +def set_paddle_flags(**kwargs): + for key, value in kwargs.items(): + if os.environ.get(key, None) is None: + os.environ[key] = str(value) + + +# NOTE(paddle-dev): All of these flags should be set before +# `import paddle`. Otherwise, it would not take any effect. +set_paddle_flags( + FLAGS_eager_delete_tensor_gb=0, # enable GC to save memory +) + +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 +from ppdet.modeling.model_input import create_feed +from ppdet.data.data_feed import create_reader +from ppdet.core.workspace import load_config, merge_config, create +from ppdet.utils.cli import print_total_cfg +from ppdet.utils.cli import ArgsParser + +import logging +FORMAT = '%(asctime)s-%(levelname)s: %(message)s' +logging.basicConfig(level=logging.INFO, format=FORMAT) +logger = logging.getLogger(__name__) + + +def main(): + """ + Main evaluate function + """ + cfg = load_config(FLAGS.config) + if 'architecture' in cfg: + main_arch = cfg.architecture + else: + raise ValueError("'architecture' not specified in config file.") + + merge_config(FLAGS.opt) + # check if set use_gpu=True in paddlepaddle cpu version + check_gpu(cfg.use_gpu) + print_total_cfg(cfg) + + 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 + place = fluid.CUDAPlace(0) if cfg.use_gpu else fluid.CPUPlace() + exe = fluid.Executor(place) + + # build program + model = create(main_arch) + startup_prog = fluid.Program() + eval_prog = fluid.Program() + with fluid.program_guard(eval_prog, startup_prog): + with fluid.unique_name.guard(): + pyreader, feed_vars = create_feed(eval_feed) + 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) + pyreader.decorate_sample_list_generator(reader, place) + + # eval already exists json file + if FLAGS.json_eval: + logger.info( + "In json_eval mode, PaddleDetection will evaluate json files in " + "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) + 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 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'] + + keys, values, cls = parse_fetches(fetches, eval_prog, extra_keys) + + # whether output bbox is normalized in model output layer + is_bbox_normalized = False + if hasattr(model, 'is_bbox_normalized') and \ + callable(model.is_bbox_normalized): + is_bbox_normalized = model.is_bbox_normalized() + + sub_eval_prog = None + sub_keys = None + sub_values = None + # build sub-program + if 'Mask' in main_arch and multi_scale_test: + 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, use_pyreader=False, sub_prog_feed=True) + 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'] + 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) + + results = eval_run(exe, compile_program, pyreader, keys, values, cls, cfg, + sub_eval_prog, sub_keys, sub_values) + + # 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) + + +if __name__ == '__main__': + parser = ArgsParser() + parser.add_argument( + "--json_eval", + 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", + default=None, + type=str, + help="Evaluation file directory, default is current directory.") + FLAGS = parser.parse_args() + main() diff --git a/tools/export_model.py b/tools/export_model.py new file mode 100644 index 0000000000000000000000000000000000000000..b0c9edac316220ca2b752fad05eec5437f698de8 --- /dev/null +++ b/tools/export_model.py @@ -0,0 +1,118 @@ +# 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 + +from paddle import fluid + +from ppdet.core.workspace import load_config, merge_config, create +from ppdet.modeling.model_input import create_feed +from ppdet.utils.cli import ArgsParser +import ppdet.utils.checkpoint as checkpoint + +import logging +FORMAT = '%(asctime)s-%(levelname)s: %(message)s' +logging.basicConfig(level=logging.INFO, format=FORMAT) +logger = logging.getLogger(__name__) + + +def prune_feed_vars(feeded_var_names, target_vars, prog): + """ + Filter out feed variables which are not in program, + pruned feed variables are only used in post processing + on model output, which are not used in program, such + as im_id to identify image order, im_shape to clip bbox + in image. + """ + exist_var_names = [] + prog = prog.clone() + prog = prog._prune(targets=target_vars) + global_block = prog.global_block() + for name in feeded_var_names: + try: + v = global_block.var(name) + exist_var_names.append(str(v.name)) + except Exception: + logger.info('save_inference_model pruned unused feed ' + 'variables {}'.format(name)) + pass + return exist_var_names + + +def save_infer_model(FLAGS, exe, feed_vars, test_fetches, infer_prog): + cfg_name = os.path.basename(FLAGS.config).split('.')[0] + save_dir = os.path.join(FLAGS.output_dir, cfg_name) + feed_var_names = [var.name for var in feed_vars.values()] + target_vars = list(test_fetches.values()) + feed_var_names = prune_feed_vars(feed_var_names, target_vars, infer_prog) + logger.info("Export inference model to {}, input: {}, output: " + "{}...".format(save_dir, feed_var_names, + [str(var.name) for var in target_vars])) + fluid.io.save_inference_model( + save_dir, + feeded_var_names=feed_var_names, + target_vars=target_vars, + executor=exe, + main_program=infer_prog, + params_filename="__params__") + + +def main(): + cfg = load_config(FLAGS.config) + + if 'architecture' in cfg: + main_arch = cfg.architecture + else: + raise ValueError("'architecture' not specified in config file.") + + 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) + + model = create(main_arch) + + startup_prog = fluid.Program() + infer_prog = fluid.Program() + with fluid.program_guard(infer_prog, startup_prog): + with fluid.unique_name.guard(): + _, feed_vars = create_feed(test_feed, use_pyreader=False) + test_fetches = model.test(feed_vars) + infer_prog = infer_prog.clone(True) + + exe.run(startup_prog) + checkpoint.load_params(exe, infer_prog, cfg.weights) + + save_infer_model(FLAGS, exe, feed_vars, test_fetches, infer_prog) + + +if __name__ == '__main__': + parser = ArgsParser() + parser.add_argument( + "--output_dir", + type=str, + default="output", + help="Directory for storing the output model files.") + FLAGS = parser.parse_args() + main() diff --git a/tools/face_eval.py b/tools/face_eval.py new file mode 100644 index 0000000000000000000000000000000000000000..f74d5ba431e2933e346d8430e9ba9b2ad20af170 --- /dev/null +++ b/tools/face_eval.py @@ -0,0 +1,298 @@ +# 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 paddle.fluid as fluid +import numpy as np +from PIL import Image +from collections import OrderedDict + +import ppdet.utils.checkpoint as checkpoint +from ppdet.utils.cli import ArgsParser +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' +logging.basicConfig(level=logging.INFO, format=FORMAT) +logger = logging.getLogger(__name__) + + +def face_img_process(image, + mean=[104., 117., 123.], + std=[127.502231, 127.502231, 127.502231]): + img = np.array(image) + img = to_chw_bgr(img) + img = img.astype('float32') + img -= np.array(mean)[:, np.newaxis, np.newaxis].astype('float32') + img /= np.array(std)[:, np.newaxis, np.newaxis].astype('float32') + img = [img] + img = np.array(img) + return img + + +def face_eval_run(exe, + compile_program, + fetches, + img_root_dir, + gt_file, + pred_dir='output/pred', + eval_mode='widerface', + multi_scale=False): + # load ground truth files + with open(gt_file, 'r') as f: + gt_lines = f.readlines() + imid2path = [] + pos_gt = 0 + while pos_gt < len(gt_lines): + name_gt = gt_lines[pos_gt].strip('\n\t').split()[0] + imid2path.append(name_gt) + pos_gt += 1 + n_gt = int(gt_lines[pos_gt].strip('\n\t').split()[0]) + pos_gt += 1 + n_gt + logger.info('The ground truth file load {} images'.format(len(imid2path))) + + dets_dist = OrderedDict() + for iter_id, im_path in enumerate(imid2path): + image_path = os.path.join(img_root_dir, im_path) + if eval_mode == 'fddb': + image_path += '.jpg' + image = Image.open(image_path).convert('RGB') + if multi_scale: + shrink, max_shrink = get_shrink(image.size[1], image.size[0]) + det0 = detect_face(exe, compile_program, fetches, image, shrink) + det1 = flip_test(exe, compile_program, fetches, image, shrink) + [det2, det3] = multi_scale_test(exe, compile_program, fetches, image, + max_shrink) + det4 = multi_scale_test_pyramid(exe, compile_program, fetches, image, + max_shrink) + det = np.row_stack((det0, det1, det2, det3, det4)) + dets = bbox_vote(det) + else: + dets = detect_face(exe, compile_program, fetches, image, 1) + if eval_mode == 'widerface': + save_widerface_bboxes(image_path, dets, pred_dir) + else: + dets_dist[im_path] = dets + if iter_id % 100 == 0: + logger.info('Test iter {}'.format(iter_id)) + if eval_mode == 'fddb': + save_fddb_bboxes(dets_dist, pred_dir) + logger.info("Finish evaluation.") + + +def detect_face(exe, compile_program, fetches, image, shrink): + image_shape = [3, image.size[1], image.size[0]] + if shrink != 1: + h, w = int(image_shape[1] * shrink), int(image_shape[2] * shrink) + image = image.resize((w, h), Image.ANTIALIAS) + image_shape = [3, h, w] + + img = face_img_process(image) + detection, = exe.run(compile_program, + feed={'image': img}, + fetch_list=[fetches['bbox']], + return_numpy=False) + detection = np.array(detection) + # layout: xmin, ymin, xmax. ymax, score + if np.prod(detection.shape) == 1: + logger.info("No face detected") + return np.array([[0, 0, 0, 0, 0]]) + det_conf = detection[:, 1] + det_xmin = image_shape[2] * detection[:, 2] / shrink + det_ymin = image_shape[1] * detection[:, 3] / shrink + det_xmax = image_shape[2] * detection[:, 4] / shrink + det_ymax = image_shape[1] * detection[:, 5] / shrink + + det = np.column_stack((det_xmin, det_ymin, det_xmax, det_ymax, det_conf)) + return det + + +def flip_test(exe, compile_program, fetches, image, shrink): + img = image.transpose(Image.FLIP_LEFT_RIGHT) + det_f = detect_face(exe, compile_program, fetches, img, shrink) + det_t = np.zeros(det_f.shape) + # image.size: [width, height] + det_t[:, 0] = image.size[0] - det_f[:, 2] + det_t[:, 1] = det_f[:, 1] + det_t[:, 2] = image.size[0] - det_f[:, 0] + det_t[:, 3] = det_f[:, 3] + det_t[:, 4] = det_f[:, 4] + return det_t + + +def multi_scale_test(exe, compile_program, fetches, image, max_shrink): + # Shrink detecting is only used to detect big faces + st = 0.5 if max_shrink >= 0.75 else 0.5 * max_shrink + det_s = detect_face(exe, compile_program, fetches, image, st) + index = np.where( + np.maximum(det_s[:, 2] - det_s[:, 0] + 1, det_s[:, 3] - det_s[:, 1] + 1) + > 30)[0] + det_s = det_s[index, :] + # Enlarge one times + bt = min(2, max_shrink) if max_shrink > 1 else (st + max_shrink) / 2 + det_b = detect_face(exe, compile_program, fetches, image, bt) + + # Enlarge small image x times for small faces + if max_shrink > 2: + bt *= 2 + while bt < max_shrink: + det_b = np.row_stack((det_b, detect_face(exe, compile_program, + fetches, image, bt))) + bt *= 2 + det_b = np.row_stack((det_b, detect_face(exe, compile_program, fetches, + image, max_shrink))) + + # Enlarged images are only used to detect small faces. + if bt > 1: + index = np.where( + np.minimum(det_b[:, 2] - det_b[:, 0] + 1, + det_b[:, 3] - det_b[:, 1] + 1) < 100)[0] + det_b = det_b[index, :] + # Shrinked images are only used to detect big faces. + else: + index = np.where( + np.maximum(det_b[:, 2] - det_b[:, 0] + 1, + det_b[:, 3] - det_b[:, 1] + 1) > 30)[0] + det_b = det_b[index, :] + return det_s, det_b + + +def multi_scale_test_pyramid(exe, compile_program, fetches, image, max_shrink): + # Use image pyramids to detect faces + det_b = detect_face(exe, compile_program, fetches, image, 0.25) + index = np.where( + np.maximum(det_b[:, 2] - det_b[:, 0] + 1, det_b[:, 3] - det_b[:, 1] + 1) + > 30)[0] + det_b = det_b[index, :] + + st = [0.75, 1.25, 1.5, 1.75] + for i in range(len(st)): + if st[i] <= max_shrink: + det_temp = detect_face(exe, compile_program, fetches, image, st[i]) + # Enlarged images are only used to detect small faces. + if st[i] > 1: + index = np.where( + np.minimum(det_temp[:, 2] - det_temp[:, 0] + 1, + det_temp[:, 3] - det_temp[:, 1] + 1) < 100)[0] + det_temp = det_temp[index, :] + # Shrinked images are only used to detect big faces. + else: + index = np.where( + np.maximum(det_temp[:, 2] - det_temp[:, 0] + 1, + det_temp[:, 3] - det_temp[:, 1] + 1) > 30)[0] + det_temp = det_temp[index, :] + det_b = np.row_stack((det_b, det_temp)) + return det_b + + +def main(): + """ + Main evaluate function + """ + cfg = load_config(FLAGS.config) + if 'architecture' in cfg: + main_arch = cfg.architecture + else: + raise ValueError("'architecture' not specified in config file.") + + merge_config(FLAGS.opt) + + # 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) + + # build program + model = create(main_arch) + startup_prog = fluid.Program() + eval_prog = fluid.Program() + with fluid.program_guard(eval_prog, startup_prog): + with fluid.unique_name.guard(): + _, feed_vars = create_feed(eval_feed, use_pyreader=False) + fetches = model.eval(feed_vars) + + eval_prog = eval_prog.clone(True) + + # load model + exe.run(startup_prog) + if 'weights' in cfg: + checkpoint.load_params(exe, eval_prog, cfg.weights) + + 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) + pred_dir = FLAGS.output_eval if FLAGS.output_eval else 'output/pred' + face_eval_run( + exe, + eval_prog, + fetches, + img_root_dir, + gt_file, + pred_dir=pred_dir, + eval_mode=FLAGS.eval_mode, + multi_scale=FLAGS.multi_scale) + + +if __name__ == '__main__': + parser = ArgsParser() + 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", + default=None, + type=str, + help="Evaluation file directory, default is current directory.") + parser.add_argument( + "-e", + "--eval_mode", + default="widerface", + type=str, + help="Evaluation mode, include `widerface` and `fddb`, default is `widerface`." + ) + parser.add_argument( + "--multi_scale", + action='store_true', + default=False, + help="If True it will select `multi_scale` evaluation. Default is `False`, it will select `single-scale` evaluation.") + FLAGS = parser.parse_args() + main() diff --git a/tools/infer.py b/tools/infer.py new file mode 100644 index 0000000000000000000000000000000000000000..ed10814846037dcabdb301e244370848c647a61b --- /dev/null +++ b/tools/infer.py @@ -0,0 +1,269 @@ +# 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 glob + +import numpy as np +from PIL import Image + + +def set_paddle_flags(**kwargs): + for key, value in kwargs.items(): + if os.environ.get(key, None) is None: + os.environ[key] = str(value) + + +# NOTE(paddle-dev): All of these flags should be set before +# `import paddle`. Otherwise, it would not take any effect. +set_paddle_flags( + FLAGS_eager_delete_tensor_gb=0, # enable GC to save memory +) + +from paddle import fluid + +from ppdet.utils.cli import print_total_cfg +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 +from ppdet.utils.check import check_gpu +from ppdet.utils.visualizer import visualize_results +import ppdet.utils.checkpoint as checkpoint + +import logging +FORMAT = '%(asctime)s-%(levelname)s: %(message)s' +logging.basicConfig(level=logging.INFO, format=FORMAT) +logger = logging.getLogger(__name__) + + +def get_save_image_name(output_dir, image_path): + """ + Get save image name from source image path. + """ + if not os.path.exists(output_dir): + os.makedirs(output_dir) + image_name = os.path.split(image_path)[-1] + name, ext = os.path.splitext(image_name) + return os.path.join(output_dir, "{}".format(name)) + ext + + +def get_test_images(infer_dir, infer_img): + """ + Get image path list in TEST mode + """ + assert infer_img is not None or infer_dir is not None, \ + "--infer_img or --infer_dir should be set" + assert infer_img is None or os.path.isfile(infer_img), \ + "{} is not a file".format(infer_img) + assert infer_dir is None or os.path.isdir(infer_dir), \ + "{} is not a directory".format(infer_dir) + images = [] + + # infer_img has a higher priority + if infer_img and os.path.isfile(infer_img): + images.append(infer_img) + return images + + infer_dir = os.path.abspath(infer_dir) + assert os.path.isdir(infer_dir), \ + "infer_dir {} is not a directory".format(infer_dir) + exts = ['jpg', 'jpeg', 'png', 'bmp'] + exts += [ext.upper() for ext in exts] + for ext in exts: + images.extend(glob.glob('{}/*.{}'.format(infer_dir, ext))) + + assert len(images) > 0, "no image found in {}".format(infer_dir) + logger.info("Found {} inference images in total.".format(len(images))) + + return images + + +def main(): + cfg = load_config(FLAGS.config) + + if 'architecture' in cfg: + main_arch = cfg.architecture + else: + raise ValueError("'architecture' not specified in config file.") + + merge_config(FLAGS.opt) + + # check if set use_gpu=True in paddlepaddle cpu version + check_gpu(cfg.use_gpu) + print_total_cfg(cfg) + + if 'test_feed' not in cfg: + test_feed = create(main_arch + 'TestFeed') + else: + test_feed = create(cfg.test_feed) + + test_images = get_test_images(FLAGS.infer_dir, FLAGS.infer_img) + test_feed.dataset.add_images(test_images) + + place = fluid.CUDAPlace(0) if cfg.use_gpu else fluid.CPUPlace() + exe = fluid.Executor(place) + + model = create(main_arch) + + startup_prog = fluid.Program() + infer_prog = fluid.Program() + with fluid.program_guard(infer_prog, startup_prog): + with fluid.unique_name.guard(): + _, feed_vars = create_feed(test_feed, use_pyreader=False) + test_fetches = model.test(feed_vars) + infer_prog = infer_prog.clone(True) + + reader = create_reader(test_feed) + feeder = fluid.DataFeeder(place=place, feed_list=feed_vars.values()) + + exe.run(startup_prog) + if cfg.weights: + checkpoint.load_params(exe, infer_prog, cfg.weights) + + # parse infer fetches + assert cfg.metric in ['COCO', 'VOC', 'WIDERFACE'], \ + "unknown metric type {}".format(cfg.metric) + extra_keys = [] + if cfg['metric'] == 'COCO': + extra_keys = ['im_info', 'im_id', 'im_shape'] + if cfg['metric'] == 'VOC' or cfg['metric'] == 'WIDERFACE': + extra_keys = ['im_id', 'im_shape'] + keys, values, _ = parse_fetches(test_fetches, infer_prog, extra_keys) + + # parse dataset category + if cfg.metric == 'COCO': + from ppdet.utils.coco_eval import bbox2out, mask2out, get_category_info + if cfg.metric == "VOC": + from ppdet.utils.voc_eval import bbox2out, get_category_info + 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) + clsid2catid, catid2name = get_category_info(anno_file, with_background, + use_default_label) + + # whether output bbox is normalized in model output layer + is_bbox_normalized = False + if hasattr(model, 'is_bbox_normalized') and \ + callable(model.is_bbox_normalized): + is_bbox_normalized = model.is_bbox_normalized() + + # use tb-paddle to log image + if FLAGS.use_tb: + from tb_paddle import SummaryWriter + tb_writer = SummaryWriter(FLAGS.tb_log_dir) + tb_image_step = 0 + tb_image_frame = 0 # each frame can display ten pictures at most. + + imid2path = reader.imid2path + for iter_id, data in enumerate(reader()): + outs = exe.run(infer_prog, + feed=feeder.feed(data), + fetch_list=values, + return_numpy=False) + res = { + k: (np.array(v), v.recursive_sequence_lengths()) + for k, v in zip(keys, outs) + } + logger.info('Infer iter {}'.format(iter_id)) + + bbox_results = None + mask_results = None + if 'bbox' in res: + bbox_results = bbox2out([res], clsid2catid, is_bbox_normalized) + if 'mask' in res: + mask_results = mask2out([res], clsid2catid, + model.mask_head.resolution) + + # visualize result + im_ids = res['im_id'][0] + for im_id in im_ids: + image_path = imid2path[int(im_id)] + image = Image.open(image_path).convert('RGB') + + # use tb-paddle to log original image + if FLAGS.use_tb: + original_image_np = np.array(image) + tb_writer.add_image( + "original/frame_{}".format(tb_image_frame), + original_image_np, + tb_image_step, + dataformats='HWC') + + image = visualize_results(image, + int(im_id), catid2name, + FLAGS.draw_threshold, bbox_results, + mask_results) + + # use tb-paddle to log image with bbox + if FLAGS.use_tb: + infer_image_np = np.array(image) + tb_writer.add_image( + "bbox/frame_{}".format(tb_image_frame), + infer_image_np, + tb_image_step, + dataformats='HWC') + tb_image_step += 1 + if tb_image_step % 10 == 0: + tb_image_step = 0 + tb_image_frame += 1 + + save_name = get_save_image_name(FLAGS.output_dir, image_path) + logger.info("Detection bbox results save in {}".format(save_name)) + image.save(save_name, quality=95) + + +if __name__ == '__main__': + parser = ArgsParser() + parser.add_argument( + "--infer_dir", + type=str, + default=None, + help="Directory for images to perform inference on.") + parser.add_argument( + "--infer_img", + type=str, + default=None, + help="Image path, has higher priority over --infer_dir") + parser.add_argument( + "--output_dir", + type=str, + default="output", + help="Directory for storing the output visualization files.") + parser.add_argument( + "--draw_threshold", + type=float, + default=0.5, + help="Threshold to reserve the result for visualization.") + parser.add_argument( + "--use_tb", + type=bool, + default=False, + help="whether to record the data to Tensorboard.") + parser.add_argument( + '--tb_log_dir', + type=str, + default="tb_log_dir/image", + help='Tensorboard logging directory for image.') + FLAGS = parser.parse_args() + main() diff --git a/tools/train.py b/tools/train.py new file mode 100644 index 0000000000000000000000000000000000000000..08e1fc63437c78722e11429d94468dcf2e5eee2c --- /dev/null +++ b/tools/train.py @@ -0,0 +1,338 @@ +# 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 time +import numpy as np +import datetime +from collections import deque + + +def set_paddle_flags(**kwargs): + for key, value in kwargs.items(): + if os.environ.get(key, None) is None: + os.environ[key] = str(value) + + +# NOTE(paddle-dev): All of these flags should be set before +# `import paddle`. Otherwise, it would not take any effect. +set_paddle_flags( + FLAGS_eager_delete_tensor_gb=0, # enable GC to save memory +) + +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.utils.cli import print_total_cfg +from ppdet.utils import dist_utils +from ppdet.utils.eval_utils import parse_fetches, eval_run, eval_results +from ppdet.utils.stats import TrainingStats +from ppdet.utils.cli import ArgsParser +from ppdet.utils.check import check_gpu +import ppdet.utils.checkpoint as checkpoint +from ppdet.modeling.model_input import create_feed + +import logging +FORMAT = '%(asctime)s-%(levelname)s: %(message)s' +logging.basicConfig(level=logging.INFO, format=FORMAT) +logger = logging.getLogger(__name__) + + +def main(): + env = os.environ + FLAGS.dist = 'PADDLE_TRAINER_ID' in env and 'PADDLE_TRAINERS_NUM' in env + if FLAGS.dist: + trainer_id = int(env['PADDLE_TRAINER_ID']) + import random + local_seed = (99 + trainer_id) + random.seed(local_seed) + np.random.seed(local_seed) + + cfg = load_config(FLAGS.config) + if 'architecture' in cfg: + main_arch = cfg.architecture + else: + raise ValueError("'architecture' not specified in config file.") + + merge_config(FLAGS.opt) + + if 'log_iter' not in cfg: + cfg.log_iter = 20 + + # check if set use_gpu=True in paddlepaddle cpu version + check_gpu(cfg.use_gpu) + if not FLAGS.dist or trainer_id == 0: + print_total_cfg(cfg) + + if cfg.use_gpu: + devices_num = fluid.core.get_cuda_device_count() + 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: + device_id = 0 + place = fluid.CUDAPlace(device_id) if cfg.use_gpu else fluid.CPUPlace() + exe = fluid.Executor(place) + + lr_builder = create('LearningRate') + optim_builder = create('OptimizerBuilder') + + # build program + startup_prog = fluid.Program() + train_prog = fluid.Program() + with fluid.program_guard(train_prog, startup_prog): + with fluid.unique_name.guard(): + model = create(main_arch) + train_pyreader, feed_vars = create_feed(train_feed) + + if FLAGS.fp16: + assert (getattr(model.backbone, 'norm_type', None) + != 'affine_channel'), \ + '--fp16 currently does not support affine channel, ' \ + ' please modify backbone settings to use batch norm' + + with mixed_precision_context(FLAGS.loss_scale, FLAGS.fp16) as ctx: + train_fetches = model.train(feed_vars) + + loss = train_fetches['loss'] + if FLAGS.fp16: + loss *= ctx.get_loss_scale_var() + lr = lr_builder() + optimizer = optim_builder(lr) + optimizer.minimize(loss) + if FLAGS.fp16: + loss /= ctx.get_loss_scale_var() + + # parse train fetches + train_keys, train_values, _ = parse_fetches(train_fetches) + train_values.append(lr) + + if FLAGS.eval: + eval_prog = fluid.Program() + with fluid.program_guard(eval_prog, startup_prog): + with fluid.unique_name.guard(): + model = create(main_arch) + eval_pyreader, feed_vars = create_feed(eval_feed) + fetches = model.eval(feed_vars) + eval_prog = eval_prog.clone(True) + + eval_reader = create_reader(eval_feed, args_path=FLAGS.dataset_dir) + eval_pyreader.decorate_sample_list_generator(eval_reader, place) + + # parse eval fetches + 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'] + if cfg.metric == 'WIDERFACE': + extra_keys = ['im_id', 'im_shape', 'gt_box'] + eval_keys, eval_values, eval_cls = parse_fetches(fetches, eval_prog, + extra_keys) + + # compile program for multi-devices + build_strategy = fluid.BuildStrategy() + build_strategy.fuse_all_optimizer_ops = False + build_strategy.fuse_elewise_add_act_ops = True + # only enable sync_bn in multi GPU devices + sync_bn = getattr(model.backbone, 'norm_type', None) == 'sync_bn' + build_strategy.sync_batch_norm = sync_bn and devices_num > 1 \ + and cfg.use_gpu + + exec_strategy = fluid.ExecutionStrategy() + # iteration number when CompiledProgram tries to drop local execution scopes. + # Set it to be 1 to save memory usages, so that unused variables in + # local execution scopes can be deleted after each iteration. + exec_strategy.num_iteration_per_drop_scope = 1 + if FLAGS.dist: + dist_utils.prepare_for_multi_process(exe, build_strategy, startup_prog, + train_prog) + exec_strategy.num_threads = 1 + + exe.run(startup_prog) + compiled_train_prog = fluid.CompiledProgram(train_prog).with_data_parallel( + loss_name=loss.name, + build_strategy=build_strategy, + exec_strategy=exec_strategy) + + if FLAGS.eval: + compiled_eval_prog = fluid.compiler.CompiledProgram(eval_prog) + + fuse_bn = getattr(model.backbone, 'norm_type', None) == 'affine_channel' + + ignore_params = cfg.finetune_exclude_pretrained_params \ + if 'finetune_exclude_pretrained_params' in cfg else [] + + start_iter = 0 + if FLAGS.resume_checkpoint: + checkpoint.load_checkpoint(exe, train_prog, FLAGS.resume_checkpoint) + start_iter = checkpoint.global_step() + elif cfg.pretrain_weights and fuse_bn and not ignore_params: + checkpoint.load_and_fusebn(exe, train_prog, cfg.pretrain_weights) + elif cfg.pretrain_weights: + 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_pyreader.decorate_sample_list_generator(train_reader, place) + + # whether output bbox is normalized in model output layer + is_bbox_normalized = False + if hasattr(model, 'is_bbox_normalized') and \ + callable(model.is_bbox_normalized): + is_bbox_normalized = model.is_bbox_normalized() + + # 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' + + train_stats = TrainingStats(cfg.log_smooth_window, train_keys) + train_pyreader.start() + start_time = time.time() + end_time = time.time() + + cfg_name = os.path.basename(FLAGS.config).split('.')[0] + save_dir = os.path.join(cfg.save_dir, cfg_name) + time_stat = deque(maxlen=cfg.log_smooth_window) + best_box_ap_list = [0.0, 0] #[map, iter] + + # use tb-paddle to log data + if FLAGS.use_tb: + from tb_paddle import SummaryWriter + tb_writer = SummaryWriter(FLAGS.tb_log_dir) + tb_loss_step = 0 + tb_mAP_step = 0 + + for it in range(start_iter, cfg.max_iters): + start_time = end_time + end_time = time.time() + time_stat.append(end_time - start_time) + time_cost = np.mean(time_stat) + eta_sec = (cfg.max_iters - it) * time_cost + eta = str(datetime.timedelta(seconds=int(eta_sec))) + outs = exe.run(compiled_train_prog, fetch_list=train_values) + stats = {k: np.array(v).mean() for k, v in zip(train_keys, outs[:-1])} + + # use tb-paddle to log loss + if FLAGS.use_tb: + if it % cfg.log_iter == 0: + for loss_name, loss_value in stats.items(): + tb_writer.add_scalar(loss_name, loss_value, tb_loss_step) + tb_loss_step += 1 + + train_stats.update(stats) + logs = train_stats.log() + if it % cfg.log_iter == 0 and (not FLAGS.dist or trainer_id == 0): + strs = 'iter: {}, lr: {:.6f}, {}, time: {:.3f}, eta: {}'.format( + it, np.mean(outs[-1]), logs, time_cost, eta) + logger.info(strs) + + if (it > 0 and it % cfg.snapshot_iter == 0 or it == cfg.max_iters - 1) \ + and (not FLAGS.dist or trainer_id == 0): + save_name = str(it) if it != cfg.max_iters - 1 else "model_final" + checkpoint.save(exe, train_prog, os.path.join(save_dir, save_name)) + + if FLAGS.eval: + # evaluation + results = eval_run(exe, compiled_eval_prog, eval_pyreader, + eval_keys, eval_values, eval_cls) + resolution = None + 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) + + # use tb_paddle to log mAP + if FLAGS.use_tb: + tb_writer.add_scalar("mAP", box_ap_stats[0], tb_mAP_step) + tb_mAP_step += 1 + + if box_ap_stats[0] > best_box_ap_list[0]: + best_box_ap_list[0] = box_ap_stats[0] + best_box_ap_list[1] = it + checkpoint.save(exe, train_prog, + os.path.join(save_dir, "best_model")) + logger.info("Best test box ap: {}, in iter: {}".format( + best_box_ap_list[0], best_box_ap_list[1])) + + train_pyreader.reset() + + +if __name__ == '__main__': + parser = ArgsParser() + parser.add_argument( + "-r", + "--resume_checkpoint", + default=None, + type=str, + help="Checkpoint path for resuming training.") + parser.add_argument( + "--fp16", + action='store_true', + default=False, + help="Enable mixed precision training.") + parser.add_argument( + "--loss_scale", + default=8., + type=float, + help="Mixed precision training loss scale.") + parser.add_argument( + "--eval", + action='store_true', + default=False, + help="Whether to perform evaluation in train") + parser.add_argument( + "--output_eval", + 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, + default=False, + help="whether to record the data to Tensorboard.") + parser.add_argument( + '--tb_log_dir', + type=str, + default="tb_log_dir/scalar", + help='Tensorboard logging directory for scalar.') + FLAGS = parser.parse_args() + main()