├── demo
├── ncnn
│ ├── android
│ │ ├── settings.gradle
│ │ ├── gradle
│ │ │ └── wrapper
│ │ │ │ ├── gradle-wrapper.jar
│ │ │ │ └── gradle-wrapper.properties
│ │ ├── app
│ │ │ ├── src
│ │ │ │ └── main
│ │ │ │ │ ├── res
│ │ │ │ │ ├── values
│ │ │ │ │ │ └── strings.xml
│ │ │ │ │ └── layout
│ │ │ │ │ │ └── main.xml
│ │ │ │ │ ├── jni
│ │ │ │ │ └── CMakeLists.txt
│ │ │ │ │ ├── AndroidManifest.xml
│ │ │ │ │ └── java
│ │ │ │ │ └── com
│ │ │ │ │ └── megvii
│ │ │ │ │ └── yoloXncnn
│ │ │ │ │ ├── YOLOXncnn.java
│ │ │ │ │ └── yoloXncnn.java
│ │ │ └── build.gradle
│ │ ├── build.gradle
│ │ ├── README.md
│ │ ├── gradlew.bat
│ │ └── gradlew
│ └── cpp
│ │ └── README.md
├── OpenVINO
│ ├── README.md
│ ├── cpp
│ │ ├── CMakeLists.txt
│ │ └── README.md
│ └── python
│ │ ├── README.md
│ │ └── openvino_inference.py
├── MegEngine
│ ├── python
│ │ ├── models
│ │ │ ├── __init__.py
│ │ │ ├── yolox.py
│ │ │ ├── yolo_fpn.py
│ │ │ ├── yolo_pafpn.py
│ │ │ └── darknet.py
│ │ ├── README.md
│ │ ├── dump.py
│ │ ├── coco_classes.py
│ │ ├── build.py
│ │ ├── convert_weights.py
│ │ ├── process.py
│ │ └── visualize.py
│ └── cpp
│ │ └── build.sh
├── TensorRT
│ ├── cpp
│ │ ├── README.md
│ │ └── CMakeLists.txt
│ └── python
│ │ └── README.md
└── ONNXRuntime
│ ├── onnx_inference.py
│ └── README.md
├── assets
├── 100.jpg
├── 89.jpg
├── 99.jpg
├── dog.jpg
├── demo.png
├── logo.png
├── zidane.jpg
└── git_fig.png
├── README.md
├── yolox
├── __pycache__
│ └── __init__.cpython-38.pyc
├── exp
│ ├── __pycache__
│ │ ├── build.cpython-38.pyc
│ │ ├── __init__.cpython-38.pyc
│ │ ├── base_exp.cpython-38.pyc
│ │ └── yolox_base.cpython-38.pyc
│ ├── __init__.py
│ ├── build.py
│ └── base_exp.py
├── utils
│ ├── __pycache__
│ │ ├── ema.cpython-38.pyc
│ │ ├── boxes.cpython-38.pyc
│ │ ├── dist.cpython-38.pyc
│ │ ├── __init__.cpython-38.pyc
│ │ ├── logger.cpython-38.pyc
│ │ ├── metric.cpython-38.pyc
│ │ ├── checkpoint.cpython-38.pyc
│ │ ├── demo_utils.cpython-38.pyc
│ │ ├── setup_env.cpython-38.pyc
│ │ ├── visualize.cpython-38.pyc
│ │ ├── lr_scheduler.cpython-38.pyc
│ │ ├── model_utils.cpython-38.pyc
│ │ └── allreduce_norm.cpython-38.pyc
│ ├── __init__.py
│ ├── checkpoint.py
│ ├── setup_env.py
│ ├── ema.py
│ ├── logger.py
│ ├── demo_utils.py
│ ├── allreduce_norm.py
│ ├── metric.py
│ ├── model_utils.py
│ ├── visualize.py
│ └── boxes.py
├── core
│ ├── __pycache__
│ │ ├── launch.cpython-38.pyc
│ │ ├── __init__.cpython-38.pyc
│ │ └── trainer.cpython-38.pyc
│ └── __init__.py
├── data
│ ├── __pycache__
│ │ ├── __init__.cpython-38.pyc
│ │ ├── samplers.cpython-38.pyc
│ │ ├── dataloading.cpython-38.pyc
│ │ ├── data_augment.cpython-38.pyc
│ │ └── data_prefetcher.cpython-38.pyc
│ ├── datasets
│ │ ├── __pycache__
│ │ │ ├── coco.cpython-38.pyc
│ │ │ ├── voc.cpython-38.pyc
│ │ │ ├── yolo.cpython-38.pyc
│ │ │ ├── __init__.cpython-38.pyc
│ │ │ ├── coco_classes.cpython-38.pyc
│ │ │ ├── voc_classes.cpython-38.pyc
│ │ │ ├── yolo_classes.cpython-38.pyc
│ │ │ ├── mosaicdetection.cpython-38.pyc
│ │ │ └── datasets_wrapper.cpython-38.pyc
│ │ ├── yolo_classes.py
│ │ ├── __init__.py
│ │ ├── voc_classes.py
│ │ ├── coco_classes.py
│ │ ├── datasets_wrapper.py
│ │ ├── coco.py
│ │ └── yolo.py
│ ├── __init__.py
│ ├── data_prefetcher.py
│ └── samplers.py
├── models
│ ├── __pycache__
│ │ ├── darknet.cpython-38.pyc
│ │ ├── losses.cpython-38.pyc
│ │ ├── yolox.cpython-38.pyc
│ │ ├── __init__.cpython-38.pyc
│ │ ├── yolo_fpn.cpython-38.pyc
│ │ ├── yolo_head.cpython-38.pyc
│ │ ├── yolo_pafpn.cpython-38.pyc
│ │ └── network_blocks.cpython-38.pyc
│ ├── __init__.py
│ ├── yolo_fpn.py
│ ├── losses.py
│ ├── yolox.py
│ └── yolo_pafpn.py
├── evaluators
│ ├── __pycache__
│ │ ├── __init__.cpython-38.pyc
│ │ ├── voc_eval.cpython-38.pyc
│ │ ├── voc_evaluator.cpython-38.pyc
│ │ ├── coco_evaluator.cpython-38.pyc
│ │ └── yolo_evaluator.cpython-38.pyc
│ └── __init__.py
├── __init__.py
└── layers
│ ├── __init__.py
│ ├── csrc
│ ├── vision.cpp
│ └── cocoeval
│ │ └── cocoeval.h
│ └── fast_coco_eval_api.py
├── exps
├── example
│ └── yolox_voc
│ │ ├── __pycache__
│ │ └── yolox_yolo_s.cpython-38.pyc
│ │ ├── yolox_voc_s.py
│ │ └── yolox_yolo_s.py
└── default
│ ├── yolox_l.py
│ ├── yolox_m.py
│ ├── yolox_s.py
│ ├── yolox_x.py
│ ├── yolox_tiny.py
│ ├── nano.py
│ └── yolov3.py
├── requirements.txt
├── setup.cfg
├── datasets
└── README.md
├── setup.py
├── tools
├── trt.py
├── export_onnx.py
└── train.py
└── docs
└── train_custom_data.md
/demo/ncnn/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/assets/100.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/assets/100.jpg
--------------------------------------------------------------------------------
/assets/89.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/assets/89.jpg
--------------------------------------------------------------------------------
/assets/99.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/assets/99.jpg
--------------------------------------------------------------------------------
/assets/dog.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/assets/dog.jpg
--------------------------------------------------------------------------------
/assets/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/assets/demo.png
--------------------------------------------------------------------------------
/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/assets/logo.png
--------------------------------------------------------------------------------
/assets/zidane.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/assets/zidane.jpg
--------------------------------------------------------------------------------
/assets/git_fig.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/assets/git_fig.png
--------------------------------------------------------------------------------
/demo/OpenVINO/README.md:
--------------------------------------------------------------------------------
1 | ## YOLOX for OpenVINO
2 |
3 | * [C++ Demo](./cpp)
4 | * [Python Demo](./python)
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | * 训练数据的格式请参考yolov5-face
2 | * 其它资料请参考yolox原始链接:https://github.com/Megvii-BaseDetection/YOLOX
3 |
4 |
--------------------------------------------------------------------------------
/yolox/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/exp/__pycache__/build.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/exp/__pycache__/build.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/utils/__pycache__/ema.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/utils/__pycache__/ema.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/core/__pycache__/launch.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/core/__pycache__/launch.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/utils/__pycache__/boxes.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/utils/__pycache__/boxes.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/utils/__pycache__/dist.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/utils/__pycache__/dist.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/core/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/core/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/core/__pycache__/trainer.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/core/__pycache__/trainer.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/data/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/data/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/data/__pycache__/samplers.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/data/__pycache__/samplers.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/exp/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/exp/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/exp/__pycache__/base_exp.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/exp/__pycache__/base_exp.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/exp/__pycache__/yolox_base.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/exp/__pycache__/yolox_base.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/models/__pycache__/darknet.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/models/__pycache__/darknet.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/models/__pycache__/losses.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/models/__pycache__/losses.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/models/__pycache__/yolox.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/models/__pycache__/yolox.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/utils/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/utils/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/utils/__pycache__/logger.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/utils/__pycache__/logger.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/utils/__pycache__/metric.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/utils/__pycache__/metric.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/data/__pycache__/dataloading.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/data/__pycache__/dataloading.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/models/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/models/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/models/__pycache__/yolo_fpn.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/models/__pycache__/yolo_fpn.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/models/__pycache__/yolo_head.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/models/__pycache__/yolo_head.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/utils/__pycache__/checkpoint.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/utils/__pycache__/checkpoint.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/utils/__pycache__/demo_utils.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/utils/__pycache__/demo_utils.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/utils/__pycache__/setup_env.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/utils/__pycache__/setup_env.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/utils/__pycache__/visualize.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/utils/__pycache__/visualize.cpython-38.pyc
--------------------------------------------------------------------------------
/demo/ncnn/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/demo/ncnn/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/yolox/data/__pycache__/data_augment.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/data/__pycache__/data_augment.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/data/datasets/__pycache__/coco.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/data/datasets/__pycache__/coco.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/data/datasets/__pycache__/voc.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/data/datasets/__pycache__/voc.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/data/datasets/__pycache__/yolo.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/data/datasets/__pycache__/yolo.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/evaluators/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/evaluators/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/evaluators/__pycache__/voc_eval.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/evaluators/__pycache__/voc_eval.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/models/__pycache__/yolo_pafpn.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/models/__pycache__/yolo_pafpn.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/utils/__pycache__/lr_scheduler.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/utils/__pycache__/lr_scheduler.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/utils/__pycache__/model_utils.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/utils/__pycache__/model_utils.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/data/__pycache__/data_prefetcher.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/data/__pycache__/data_prefetcher.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/models/__pycache__/network_blocks.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/models/__pycache__/network_blocks.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/utils/__pycache__/allreduce_norm.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/utils/__pycache__/allreduce_norm.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 |
4 | from .utils import configure_module
5 |
6 | configure_module()
7 |
8 | __version__ = "0.1.0"
9 |
--------------------------------------------------------------------------------
/yolox/data/datasets/__pycache__/__init__.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/data/datasets/__pycache__/__init__.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/evaluators/__pycache__/voc_evaluator.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/evaluators/__pycache__/voc_evaluator.cpython-38.pyc
--------------------------------------------------------------------------------
/demo/ncnn/android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | yoloXncnn
4 |
5 |
--------------------------------------------------------------------------------
/yolox/data/datasets/__pycache__/coco_classes.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/data/datasets/__pycache__/coco_classes.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/data/datasets/__pycache__/voc_classes.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/data/datasets/__pycache__/voc_classes.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/data/datasets/__pycache__/yolo_classes.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/data/datasets/__pycache__/yolo_classes.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/evaluators/__pycache__/coco_evaluator.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/evaluators/__pycache__/coco_evaluator.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/evaluators/__pycache__/yolo_evaluator.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/evaluators/__pycache__/yolo_evaluator.cpython-38.pyc
--------------------------------------------------------------------------------
/exps/example/yolox_voc/__pycache__/yolox_yolo_s.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/exps/example/yolox_voc/__pycache__/yolox_yolo_s.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/data/datasets/__pycache__/mosaicdetection.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/data/datasets/__pycache__/mosaicdetection.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/data/datasets/__pycache__/datasets_wrapper.cpython-38.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xialuxi/yolox-face-landmarks/HEAD/yolox/data/datasets/__pycache__/datasets_wrapper.cpython-38.pyc
--------------------------------------------------------------------------------
/yolox/core/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | from .launch import launch
6 | from .trainer import Trainer
7 |
--------------------------------------------------------------------------------
/yolox/layers/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | from .fast_coco_eval_api import COCOeval_opt
6 |
--------------------------------------------------------------------------------
/yolox/data/datasets/yolo_classes.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | YOLO_CLASSES = (
6 | "face",
7 | "mask",
8 | )
9 |
--------------------------------------------------------------------------------
/yolox/exp/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | from .base_exp import BaseExp
6 | from .build import get_exp
7 | from .yolox_base import Exp
8 |
--------------------------------------------------------------------------------
/yolox/evaluators/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | from .coco_evaluator import COCOEvaluator
6 | from .voc_evaluator import VOCEvaluator
7 | from .yolo_evaluator import YOLOEvaluator
--------------------------------------------------------------------------------
/demo/ncnn/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Aug 25 10:34:48 CST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | # TODO: Update with exact module version
2 | numpy
3 | torch>=1.7
4 | opencv_python
5 | loguru
6 | scikit-image
7 | tqdm
8 | torchvision
9 | Pillow
10 | thop
11 | ninja
12 | tabulate
13 | tensorboard
14 |
15 | # verified versions
16 | onnx==1.8.1
17 | onnxruntime==1.8.0
18 | onnx-simplifier==0.3.5
--------------------------------------------------------------------------------
/demo/MegEngine/python/models/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | from .darknet import CSPDarknet, Darknet
6 | from .yolo_fpn import YOLOFPN
7 | from .yolo_head import YOLOXHead
8 | from .yolo_pafpn import YOLOPAFPN
9 | from .yolox import YOLOX
10 |
--------------------------------------------------------------------------------
/yolox/models/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | from .darknet import CSPDarknet, Darknet
6 | from .losses import IOUloss
7 | from .yolo_fpn import YOLOFPN
8 | from .yolo_head import YOLOXHead
9 | from .yolo_pafpn import YOLOPAFPN
10 | from .yolox import YOLOX
11 |
--------------------------------------------------------------------------------
/yolox/data/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | from .data_augment import TrainTransform, ValTransform
6 | from .data_prefetcher import DataPrefetcher
7 | from .dataloading import DataLoader, get_yolox_datadir
8 | from .datasets import *
9 | from .samplers import InfiniteSampler, YoloBatchSampler
10 |
--------------------------------------------------------------------------------
/demo/ncnn/android/app/src/main/jni/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | project(yoloXncnn)
2 |
3 | cmake_minimum_required(VERSION 3.4.1)
4 |
5 | set(ncnn_DIR ${CMAKE_SOURCE_DIR}/ncnn-20210525-android-vulkan/${ANDROID_ABI}/lib/cmake/ncnn)
6 | find_package(ncnn REQUIRED)
7 |
8 | add_library(yoloXncnn SHARED yoloXncnn_jni.cpp)
9 |
10 | target_link_libraries(yoloXncnn
11 | ncnn
12 |
13 | jnigraphics
14 | )
15 |
--------------------------------------------------------------------------------
/demo/ncnn/android/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | jcenter()
5 | google()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:3.5.0'
9 | }
10 | }
11 |
12 | allprojects {
13 | repositories {
14 | jcenter()
15 | google()
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/exps/default/yolox_l.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | import os
6 |
7 | from yolox.exp import Exp as MyExp
8 |
9 |
10 | class Exp(MyExp):
11 | def __init__(self):
12 | super(Exp, self).__init__()
13 | self.depth = 1.0
14 | self.width = 1.0
15 | self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]
16 |
--------------------------------------------------------------------------------
/exps/default/yolox_m.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | import os
6 |
7 | from yolox.exp import Exp as MyExp
8 |
9 |
10 | class Exp(MyExp):
11 | def __init__(self):
12 | super(Exp, self).__init__()
13 | self.depth = 0.67
14 | self.width = 0.75
15 | self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]
16 |
--------------------------------------------------------------------------------
/exps/default/yolox_s.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | import os
6 |
7 | from yolox.exp import Exp as MyExp
8 |
9 |
10 | class Exp(MyExp):
11 | def __init__(self):
12 | super(Exp, self).__init__()
13 | self.depth = 0.33
14 | self.width = 0.50
15 | self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]
16 |
--------------------------------------------------------------------------------
/exps/default/yolox_x.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | import os
6 |
7 | from yolox.exp import Exp as MyExp
8 |
9 |
10 | class Exp(MyExp):
11 | def __init__(self):
12 | super(Exp, self).__init__()
13 | self.depth = 1.33
14 | self.width = 1.25
15 | self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]
16 |
--------------------------------------------------------------------------------
/yolox/data/datasets/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | from .yolo import YOLODataset
6 | from .yolo_classes import YOLO_CLASSES
7 | from .coco import COCODataset
8 | from .coco_classes import COCO_CLASSES
9 | from .datasets_wrapper import ConcatDataset, Dataset, MixConcatDataset
10 | from .mosaicdetection import MosaicDetection
11 | from .voc import VOCDetection
12 |
--------------------------------------------------------------------------------
/yolox/utils/__init__.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | from .allreduce_norm import *
6 | from .boxes import *
7 | from .checkpoint import load_ckpt, save_checkpoint
8 | from .demo_utils import *
9 | from .dist import *
10 | from .ema import ModelEMA
11 | from .logger import setup_logger
12 | from .lr_scheduler import LRScheduler
13 | from .metric import *
14 | from .model_utils import *
15 | from .setup_env import *
16 | from .visualize import *
17 |
--------------------------------------------------------------------------------
/yolox/data/datasets/voc_classes.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | # VOC_CLASSES = ( '__background__', # always index 0
6 | VOC_CLASSES = (
7 | "aeroplane",
8 | "bicycle",
9 | "bird",
10 | "boat",
11 | "bottle",
12 | "bus",
13 | "car",
14 | "cat",
15 | "chair",
16 | "cow",
17 | "diningtable",
18 | "dog",
19 | "horse",
20 | "motorbike",
21 | "person",
22 | "pottedplant",
23 | "sheep",
24 | "sofa",
25 | "train",
26 | "tvmonitor",
27 | )
28 |
--------------------------------------------------------------------------------
/exps/default/yolox_tiny.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | import os
6 |
7 | from yolox.exp import Exp as MyExp
8 |
9 |
10 | class Exp(MyExp):
11 | def __init__(self):
12 | super(Exp, self).__init__()
13 | self.depth = 0.33
14 | self.width = 0.375
15 | self.scale = (0.5, 1.5)
16 | self.random_size = (10, 20)
17 | self.test_size = (416, 416)
18 | self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]
19 | self.enable_mixup = False
20 |
--------------------------------------------------------------------------------
/yolox/layers/csrc/vision.cpp:
--------------------------------------------------------------------------------
1 | #include "cocoeval/cocoeval.h"
2 |
3 | PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
4 | m.def("COCOevalAccumulate", &COCOeval::Accumulate, "COCOeval::Accumulate");
5 | m.def(
6 | "COCOevalEvaluateImages",
7 | &COCOeval::EvaluateImages,
8 | "COCOeval::EvaluateImages");
9 | pybind11::class_(m, "InstanceAnnotation")
10 | .def(pybind11::init());
11 | pybind11::class_(m, "ImageEvaluation")
12 | .def(pybind11::init<>());
13 | }
14 |
--------------------------------------------------------------------------------
/demo/OpenVINO/cpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.4.1)
2 | set(CMAKE_CXX_STANDARD 14)
3 |
4 | project(yolox_openvino_demo)
5 |
6 | find_package(OpenCV REQUIRED)
7 | find_package(InferenceEngine REQUIRED)
8 | find_package(ngraph REQUIRED)
9 |
10 | include_directories(
11 | ${OpenCV_INCLUDE_DIRS}
12 | ${CMAKE_CURRENT_SOURCE_DIR}
13 | ${CMAKE_CURRENT_BINARY_DIR}
14 | )
15 |
16 | add_executable(yolox_openvino yolox_openvino.cpp)
17 |
18 | target_link_libraries(
19 | yolox_openvino
20 | ${InferenceEngine_LIBRARIES}
21 | ${NGRAPH_LIBRARIES}
22 | ${OpenCV_LIBS}
23 | )
--------------------------------------------------------------------------------
/demo/ncnn/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 24
5 | buildToolsVersion "29.0.2"
6 |
7 | defaultConfig {
8 | applicationId "com.megvii.yoloXncnn"
9 | archivesBaseName = "$applicationId"
10 |
11 | ndk {
12 | moduleName "ncnn"
13 | abiFilters "armeabi-v7a", "arm64-v8a"
14 | }
15 | minSdkVersion 24
16 | }
17 |
18 | externalNativeBuild {
19 | cmake {
20 | version "3.10.2"
21 | path file('src/main/jni/CMakeLists.txt')
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [isort]
2 | line_length = 100
3 | multi_line_output = 3
4 | balanced_wrapping = True
5 | known_standard_library = setuptools
6 | known_third_party = tqdm,loguru
7 | known_data_processing = cv2,numpy,scipy,PIL,matplotlib,scikit_image
8 | known_datasets = pycocotools
9 | known_deeplearning = torch,torchvision,caffe2,onnx,apex,timm,thop,torch2trt,tensorrt,openvino,onnxruntime
10 | known_myself = yolox
11 | sections = FUTURE,STDLIB,THIRDPARTY,data_processing,datasets,deeplearning,myself,FIRSTPARTY,LOCALFOLDER
12 | no_lines_before=STDLIB,THIRDPARTY,datasets
13 | default_section = FIRSTPARTY
14 |
15 | [flake8]
16 | max-line-length = 100
17 | max-complexity = 18
18 | exclude = __init__.py
19 |
--------------------------------------------------------------------------------
/demo/ncnn/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/demo/ncnn/android/app/src/main/java/com/megvii/yoloXncnn/YOLOXncnn.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) Megvii, Inc. and its affiliates. All rights reserved.
2 |
3 | package com.megvii.yoloXncnn;
4 |
5 | import android.content.res.AssetManager;
6 | import android.graphics.Bitmap;
7 |
8 | public class YOLOXncnn
9 | {
10 | public native boolean Init(AssetManager mgr);
11 |
12 | public class Obj
13 | {
14 | public float x;
15 | public float y;
16 | public float w;
17 | public float h;
18 | public String label;
19 | public float prob;
20 | }
21 |
22 | public native Obj[] Detect(Bitmap bitmap, boolean use_gpu);
23 |
24 | static {
25 | System.loadLibrary("yoloXncnn");
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/demo/ncnn/android/app/src/main/java/com/megvii/yoloXncnn/yoloXncnn.java:
--------------------------------------------------------------------------------
1 | // Copyright (C) Megvii, Inc. and its affiliates. All rights reserved.
2 |
3 | package com.megvii.yoloXncnn;
4 |
5 | import android.content.res.AssetManager;
6 | import android.graphics.Bitmap;
7 |
8 | public class YOLOXncnn
9 | {
10 | public native boolean Init(AssetManager mgr);
11 |
12 | public class Obj
13 | {
14 | public float x;
15 | public float y;
16 | public float w;
17 | public float h;
18 | public String label;
19 | public float prob;
20 | }
21 |
22 | public native Obj[] Detect(Bitmap bitmap, boolean use_gpu);
23 |
24 | static {
25 | System.loadLibrary("yoloXncnn");
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/datasets/README.md:
--------------------------------------------------------------------------------
1 | # Prepare datasets
2 |
3 | If you have a dataset directory, you could use os environment variable named `YOLOX_DATADIR`. Under this directory, YOLOX will look for datasets in the structure described below, if needed.
4 | ```
5 | $YOLOX_DATADIR/
6 | COCO/
7 | ```
8 | You can set the location for builtin datasets by
9 | ```shell
10 | export YOLOX_DATADIR=/path/to/your/datasets
11 | ```
12 | If `YOLOX_DATADIR` is not set, the default value of dataset directory is `./datasets` relative to your current working directory.
13 |
14 | ## Expected dataset structure for [COCO detection](https://cocodataset.org/#download):
15 |
16 | ```
17 | COCO/
18 | annotations/
19 | instances_{train,val}2017.json
20 | {train,val}2017/
21 | # image files that are mentioned in the corresponding json
22 | ```
23 |
24 | You can use the 2014 version of the dataset as well.
25 |
--------------------------------------------------------------------------------
/demo/MegEngine/python/README.md:
--------------------------------------------------------------------------------
1 | # YOLOX-Python-MegEngine
2 |
3 | Python version of YOLOX object detection base on [MegEngine](https://github.com/MegEngine/MegEngine).
4 |
5 | ## Tutorial
6 |
7 | ### Step1: install requirements
8 |
9 | ```
10 | python3 -m pip install megengine -f https://megengine.org.cn/whl/mge.html
11 | ```
12 |
13 | ### Step2: convert checkpoint weights from torch's path file
14 |
15 | ```
16 | python3 convert_weights.py -w yolox_s.pth.tar -o yolox_s_mge.pkl
17 | ```
18 |
19 | ### Step3: run demo
20 |
21 | This part is the same as torch's python demo, but no need to specify device.
22 |
23 | ```
24 | python3 demo.py image -n yolox-s -c yolox_s_mge.pkl --path ../../../assets/dog.jpg --conf 0.25 --nms 0.45 --tsize 640 --save_result
25 | ```
26 |
27 | ### [Optional]Step4: dump model for cpp inference
28 |
29 | > **Note**: result model is dumped with `optimize_for_inference` and `enable_fuse_conv_bias_nonlinearity`.
30 |
31 | ```
32 | python3 dump.py -n yolox-s -c yolox_s_mge.pkl --dump_path yolox_s.mge
33 | ```
34 |
--------------------------------------------------------------------------------
/demo/MegEngine/python/models/yolox.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- encoding: utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import megengine.module as M
6 |
7 | from .yolo_head import YOLOXHead
8 | from .yolo_pafpn import YOLOPAFPN
9 |
10 |
11 | class YOLOX(M.Module):
12 | """
13 | YOLOX model module. The module list is defined by create_yolov3_modules function.
14 | The network returns loss values from three YOLO layers during training
15 | and detection results during test.
16 | """
17 |
18 | def __init__(self, backbone=None, head=None):
19 | super().__init__()
20 | if backbone is None:
21 | backbone = YOLOPAFPN()
22 | if head is None:
23 | head = YOLOXHead(80)
24 |
25 | self.backbone = backbone
26 | self.head = head
27 |
28 | def forward(self, x):
29 | # fpn output content features of [dark3, dark4, dark5]
30 | fpn_outs = self.backbone(x)
31 | assert not self.training
32 | outputs = self.head(fpn_outs)
33 |
34 | return outputs
35 |
--------------------------------------------------------------------------------
/demo/TensorRT/cpp/README.md:
--------------------------------------------------------------------------------
1 | # YOLOX-TensorRT in C++
2 |
3 | As YOLOX models is easy to converted to tensorrt using [torch2trt gitrepo](https://github.com/NVIDIA-AI-IOT/torch2trt),
4 | our C++ demo will not include the model converting or constructing like other tenorrt demos.
5 |
6 |
7 | ## Step 1: Prepare serialized engine file
8 |
9 | Follow the trt [python demo README](../python/README.md) to convert and save the serialized engine file.
10 |
11 | Check the 'model_trt.engine' file generated from Step 1, which will automatically saved at the current demo dir.
12 |
13 |
14 | ## Step 2: build the demo
15 |
16 | Please follow the [TensorRT Installation Guide](https://docs.nvidia.com/deeplearning/tensorrt/install-guide/index.html) to install TensorRT.
17 |
18 | Install opencv with ```sudo apt-get install libopencv-dev```.
19 |
20 | build the demo:
21 |
22 | ```shell
23 | mkdir build
24 | cd build
25 | cmake ..
26 | make
27 | ```
28 |
29 | Then run the demo:
30 |
31 | ```shell
32 | ./yolox ../model_trt.engine -i ../../../../assets/dog.jpg
33 | ```
34 |
35 | or
36 |
37 | ```shell
38 | ./yolox -i
39 | ```
40 |
--------------------------------------------------------------------------------
/demo/ncnn/android/README.md:
--------------------------------------------------------------------------------
1 | # YOLOX-Android-ncnn
2 |
3 | Andoird app of YOLOX object detection base on [ncnn](https://github.com/Tencent/ncnn)
4 |
5 |
6 | ## Tutorial
7 |
8 | ### Step1
9 |
10 | Download ncnn-android-vulkan.zip from [releases of ncnn](https://github.com/Tencent/ncnn/releases). This repo uses
11 | [20210525 release](https://github.com/Tencent/ncnn/releases/download/20210525/ncnn-20210525-android-vulkan.zip) for building.
12 |
13 | ### Step2
14 |
15 | After downloading, please extract your zip file. Then, there are two ways to finish this step:
16 | * put your extracted directory into **app/src/main/jni**
17 | * change the **ncnn_DIR** path in **app/src/main/jni/CMakeLists.txt** to your extracted directory
18 |
19 | ### Step3
20 | Download example param and bin file from [onedrive](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/ESXBH_GSSmFMszWJ6YG2VkQB5cWDfqVWXgk0D996jH0rpQ?e=qzEqUh) or [github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_s_ncnn.tar.gz). Unzip the file to **app/src/main/assets**.
21 |
22 | ### Step4
23 | Open this project with Android Studio, build it and enjoy!
24 |
25 | ## Reference
26 |
27 | * [ncnn-android-yolov5](https://github.com/nihui/ncnn-android-yolov5)
28 |
--------------------------------------------------------------------------------
/demo/TensorRT/cpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.6)
2 |
3 | project(yolox)
4 |
5 | add_definitions(-std=c++11)
6 |
7 | option(CUDA_USE_STATIC_CUDA_RUNTIME OFF)
8 | set(CMAKE_CXX_STANDARD 11)
9 | set(CMAKE_BUILD_TYPE Debug)
10 |
11 | find_package(CUDA REQUIRED)
12 |
13 | include_directories(${PROJECT_SOURCE_DIR}/include)
14 | # include and link dirs of cuda and tensorrt, you need adapt them if yours are different
15 | # cuda
16 | include_directories(/data/cuda/cuda-10.2/cuda/include)
17 | link_directories(/data/cuda/cuda-10.2/cuda/lib64)
18 | # cudnn
19 | include_directories(/data/cuda/cuda-10.2/cudnn/v8.0.4/include)
20 | link_directories(/data/cuda/cuda-10.2/cudnn/v8.0.4/lib64)
21 | # tensorrt
22 | include_directories(/data/cuda/cuda-10.2/TensorRT/v7.2.1.6/include)
23 | link_directories(/data/cuda/cuda-10.2/TensorRT/v7.2.1.6/lib)
24 |
25 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Ofast -Wfatal-errors -D_MWAITXINTRIN_H_INCLUDED")
26 |
27 | find_package(OpenCV)
28 | include_directories(${OpenCV_INCLUDE_DIRS})
29 |
30 | add_executable(yolox ${PROJECT_SOURCE_DIR}/yolox.cpp)
31 | target_link_libraries(yolox nvinfer)
32 | target_link_libraries(yolox cudart)
33 | target_link_libraries(yolox ${OpenCV_LIBS})
34 |
35 | add_definitions(-O2 -pthread)
36 |
37 |
--------------------------------------------------------------------------------
/demo/ncnn/android/app/src/main/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
17 |
22 |
27 |
28 |
29 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/exps/default/nano.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | import os
6 | import torch.nn as nn
7 |
8 | from yolox.exp import Exp as MyExp
9 |
10 |
11 | class Exp(MyExp):
12 | def __init__(self):
13 | super(Exp, self).__init__()
14 | self.depth = 0.33
15 | self.width = 0.25
16 | self.scale = (0.5, 1.5)
17 | self.random_size = (10, 20)
18 | self.test_size = (416, 416)
19 | self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]
20 | self.enable_mixup = False
21 |
22 | def get_model(self, sublinear=False):
23 |
24 | def init_yolo(M):
25 | for m in M.modules():
26 | if isinstance(m, nn.BatchNorm2d):
27 | m.eps = 1e-3
28 | m.momentum = 0.03
29 | if "model" not in self.__dict__:
30 | from yolox.models import YOLOX, YOLOPAFPN, YOLOXHead
31 | in_channels = [256, 512, 1024]
32 | # NANO model use depthwise = True, which is main difference.
33 | backbone = YOLOPAFPN(self.depth, self.width, in_channels=in_channels, depthwise=True)
34 | head = YOLOXHead(self.num_classes, self.width, in_channels=in_channels, depthwise=True)
35 | self.model = YOLOX(backbone, head)
36 |
37 | self.model.apply(init_yolo)
38 | self.model.head.initialize_biases(1e-2)
39 | return self.model
40 |
--------------------------------------------------------------------------------
/demo/MegEngine/python/dump.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | import argparse
6 |
7 | import megengine as mge
8 | import numpy as np
9 | from megengine import jit
10 |
11 | from build import build_and_load
12 |
13 |
14 | def make_parser():
15 | parser = argparse.ArgumentParser("YOLOX Demo Dump")
16 | parser.add_argument("-n", "--name", type=str, default="yolox-s", help="model name")
17 | parser.add_argument("-c", "--ckpt", default=None, type=str, help="ckpt for eval")
18 | parser.add_argument(
19 | "--dump_path", default="model.mge", help="path to save the dumped model"
20 | )
21 | return parser
22 |
23 |
24 | def dump_static_graph(model, graph_name="model.mge"):
25 | model.eval()
26 | model.head.decode_in_inference = False
27 |
28 | data = mge.Tensor(np.random.random((1, 3, 640, 640)))
29 |
30 | @jit.trace(capture_as_const=True)
31 | def pred_func(data):
32 | outputs = model(data)
33 | return outputs
34 |
35 | pred_func(data)
36 | pred_func.dump(
37 | graph_name,
38 | arg_names=["data"],
39 | optimize_for_inference=True,
40 | enable_fuse_conv_bias_nonlinearity=True,
41 | )
42 |
43 |
44 | def main(args):
45 | model = build_and_load(args.ckpt, name=args.name)
46 | dump_static_graph(model, args.dump_path)
47 |
48 |
49 | if __name__ == "__main__":
50 | args = make_parser().parse_args()
51 | main(args)
52 |
--------------------------------------------------------------------------------
/yolox/utils/checkpoint.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 | from loguru import logger
5 |
6 | import torch
7 |
8 | import os
9 | import shutil
10 |
11 |
12 | def load_ckpt(model, ckpt):
13 | model_state_dict = model.state_dict()
14 | load_dict = {}
15 | for key_model, v in model_state_dict.items():
16 | if key_model not in ckpt:
17 | logger.warning(
18 | "{} is not in the ckpt. Please double check and see if this is desired.".format(
19 | key_model
20 | )
21 | )
22 | continue
23 | v_ckpt = ckpt[key_model]
24 | if v.shape != v_ckpt.shape:
25 | logger.warning(
26 | "Shape of {} in checkpoint is {}, while shape of {} in model is {}.".format(
27 | key_model, v_ckpt.shape, key_model, v.shape
28 | )
29 | )
30 | continue
31 | load_dict[key_model] = v_ckpt
32 |
33 | model.load_state_dict(load_dict, strict=False)
34 | return model
35 |
36 |
37 | def save_checkpoint(state, is_best, save_dir, model_name=""):
38 | if not os.path.exists(save_dir):
39 | os.makedirs(save_dir)
40 | filename = os.path.join(save_dir, model_name + "_ckpt.pth.tar")
41 | torch.save(state, filename)
42 | if is_best:
43 | best_filename = os.path.join(save_dir, "best_ckpt.pth.tar")
44 | shutil.copyfile(filename, best_filename)
45 |
--------------------------------------------------------------------------------
/yolox/data/datasets/coco_classes.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | COCO_CLASSES = (
6 | "person",
7 | "bicycle",
8 | "car",
9 | "motorcycle",
10 | "airplane",
11 | "bus",
12 | "train",
13 | "truck",
14 | "boat",
15 | "traffic light",
16 | "fire hydrant",
17 | "stop sign",
18 | "parking meter",
19 | "bench",
20 | "bird",
21 | "cat",
22 | "dog",
23 | "horse",
24 | "sheep",
25 | "cow",
26 | "elephant",
27 | "bear",
28 | "zebra",
29 | "giraffe",
30 | "backpack",
31 | "umbrella",
32 | "handbag",
33 | "tie",
34 | "suitcase",
35 | "frisbee",
36 | "skis",
37 | "snowboard",
38 | "sports ball",
39 | "kite",
40 | "baseball bat",
41 | "baseball glove",
42 | "skateboard",
43 | "surfboard",
44 | "tennis racket",
45 | "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",
66 | "dining table",
67 | "toilet",
68 | "tv",
69 | "laptop",
70 | "mouse",
71 | "remote",
72 | "keyboard",
73 | "cell phone",
74 | "microwave",
75 | "oven",
76 | "toaster",
77 | "sink",
78 | "refrigerator",
79 | "book",
80 | "clock",
81 | "vase",
82 | "scissors",
83 | "teddy bear",
84 | "hair drier",
85 | "toothbrush",
86 | )
87 |
--------------------------------------------------------------------------------
/demo/MegEngine/python/coco_classes.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | COCO_CLASSES = (
6 | "person",
7 | "bicycle",
8 | "car",
9 | "motorcycle",
10 | "airplane",
11 | "bus",
12 | "train",
13 | "truck",
14 | "boat",
15 | "traffic light",
16 | "fire hydrant",
17 | "stop sign",
18 | "parking meter",
19 | "bench",
20 | "bird",
21 | "cat",
22 | "dog",
23 | "horse",
24 | "sheep",
25 | "cow",
26 | "elephant",
27 | "bear",
28 | "zebra",
29 | "giraffe",
30 | "backpack",
31 | "umbrella",
32 | "handbag",
33 | "tie",
34 | "suitcase",
35 | "frisbee",
36 | "skis",
37 | "snowboard",
38 | "sports ball",
39 | "kite",
40 | "baseball bat",
41 | "baseball glove",
42 | "skateboard",
43 | "surfboard",
44 | "tennis racket",
45 | "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",
66 | "dining table",
67 | "toilet",
68 | "tv",
69 | "laptop",
70 | "mouse",
71 | "remote",
72 | "keyboard",
73 | "cell phone",
74 | "microwave",
75 | "oven",
76 | "toaster",
77 | "sink",
78 | "refrigerator",
79 | "book",
80 | "clock",
81 | "vase",
82 | "scissors",
83 | "teddy bear",
84 | "hair drier",
85 | "toothbrush",
86 | )
87 |
--------------------------------------------------------------------------------
/yolox/exp/build.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import importlib
6 | import os
7 | import sys
8 |
9 |
10 | def get_exp_by_file(exp_file):
11 | try:
12 | sys.path.append(os.path.dirname(exp_file))
13 | current_exp = importlib.import_module(os.path.basename(exp_file).split(".")[0])
14 | exp = current_exp.Exp()
15 | except Exception:
16 | raise ImportError("{} doesn't contains class named 'Exp'".format(exp_file))
17 | return exp
18 |
19 |
20 | def get_exp_by_name(exp_name):
21 | import yolox
22 |
23 | yolox_path = os.path.dirname(os.path.dirname(yolox.__file__))
24 | filedict = {
25 | "yolox-s": "yolox_s.py",
26 | "yolox-m": "yolox_m.py",
27 | "yolox-l": "yolox_l.py",
28 | "yolox-x": "yolox_x.py",
29 | "yolox-tiny": "yolox_tiny.py",
30 | "yolox-nano": "nano.py",
31 | "yolov3": "yolov3.py",
32 | }
33 | filename = filedict[exp_name]
34 | exp_path = os.path.join(yolox_path, "exps", "default", filename)
35 | return get_exp_by_file(exp_path)
36 |
37 |
38 | def get_exp(exp_file, exp_name):
39 | """
40 | get Exp object by file or name. If exp_file and exp_name
41 | are both provided, get Exp by exp_file.
42 |
43 | Args:
44 | exp_file (str): file path of experiment.
45 | exp_name (str): name of experiment. "yolo-s",
46 | """
47 | assert (
48 | exp_file is not None or exp_name is not None
49 | ), "plz provide exp file or exp name."
50 | if exp_file is not None:
51 | return get_exp_by_file(exp_file)
52 | else:
53 | return get_exp_by_name(exp_name)
54 |
--------------------------------------------------------------------------------
/demo/TensorRT/python/README.md:
--------------------------------------------------------------------------------
1 | # YOLOX-TensorRT in Python
2 |
3 | This toturial includes a Python demo for TensorRT.
4 |
5 | ## Install TensorRT Toolkit
6 |
7 | Please follow the [TensorRT Installation Guide](https://docs.nvidia.com/deeplearning/tensorrt/install-guide/index.html) and [torch2trt gitrepo](https://github.com/NVIDIA-AI-IOT/torch2trt) to install TensorRT and torch2trt.
8 |
9 | ## Convert model
10 |
11 | YOLOX models can be easily conveted to TensorRT models using torch2trt
12 |
13 | If you want to convert our model, use the flag -n to specify a model name:
14 | ```shell
15 | python tools/trt.py -n -c
16 | ```
17 | For example:
18 | ```shell
19 | python tools/trt.py -n yolox-s -c your_ckpt.pth.tar
20 | ```
21 | can be: yolox-nano, yolox-tiny. yolox-s, yolox-m, yolox-l, yolox-x.
22 |
23 | If you want to convert your customized model, use the flag -f to specify you exp file:
24 | ```shell
25 | python tools/trt.py -f -c
26 | ```
27 | For example:
28 | ```shell
29 | python tools/trt.py -f /path/to/your/yolox/exps/yolox_s.py -c your_ckpt.pth.tar
30 | ```
31 | *yolox_s.py* can be any exp file modified by you.
32 |
33 | The converted model and the serialized engine file (for C++ demo) will be saved on your experiment output dir.
34 |
35 | ## Demo
36 |
37 | The TensorRT python demo is merged on our pytorch demo file, so you can run the pytorch demo command with ```--trt```.
38 |
39 | ```shell
40 | python tools/demo.py image -n yolox-s --trt --save_result
41 | ```
42 | or
43 | ```shell
44 | python tools/demo.py image -f exps/default/yolox_s.py --trt --save_result
45 | ```
46 |
47 |
--------------------------------------------------------------------------------
/demo/MegEngine/python/build.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 |
4 | import megengine as mge
5 | import megengine.module as M
6 | from megengine import jit
7 |
8 | from models.yolo_fpn import YOLOFPN
9 | from models.yolo_head import YOLOXHead
10 | from models.yolo_pafpn import YOLOPAFPN
11 | from models.yolox import YOLOX
12 |
13 |
14 | def build_yolox(name="yolox-s"):
15 | num_classes = 80
16 |
17 | # value meaning: depth, width
18 | param_dict = {
19 | "yolox-nano": (0.33, 0.25),
20 | "yolox-tiny": (0.33, 0.375),
21 | "yolox-s": (0.33, 0.50),
22 | "yolox-m": (0.67, 0.75),
23 | "yolox-l": (1.0, 1.0),
24 | "yolox-x": (1.33, 1.25),
25 | }
26 | if name == "yolov3":
27 | depth = 1.0
28 | width = 1.0
29 | backbone = YOLOFPN()
30 | head = YOLOXHead(num_classes, width, in_channels=[128, 256, 512], act="lrelu")
31 | model = YOLOX(backbone, head)
32 | else:
33 | assert name in param_dict
34 | kwargs = {}
35 | depth, width = param_dict[name]
36 | if name == "yolox-nano":
37 | kwargs["depthwise"] = True
38 | in_channels = [256, 512, 1024]
39 | backbone = YOLOPAFPN(depth, width, in_channels=in_channels, **kwargs)
40 | head = YOLOXHead(num_classes, width, in_channels=in_channels, **kwargs)
41 | model = YOLOX(backbone, head)
42 |
43 | for m in model.modules():
44 | if isinstance(m, M.BatchNorm2d):
45 | m.eps = 1e-3
46 |
47 | return model
48 |
49 |
50 | def build_and_load(weight_file, name="yolox-s"):
51 | model = build_yolox(name)
52 | model_weights = mge.load(weight_file)
53 | model.load_state_dict(model_weights, strict=False)
54 | return model
55 |
--------------------------------------------------------------------------------
/yolox/utils/setup_env.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import cv2
6 |
7 | import os
8 | import subprocess
9 |
10 | __all__ = ["configure_nccl", "configure_module"]
11 |
12 |
13 | def configure_nccl():
14 | """Configure multi-machine environment variables of NCCL."""
15 | os.environ["NCCL_LAUNCH_MODE"] = "PARALLEL"
16 | os.environ["NCCL_IB_HCA"] = subprocess.getoutput(
17 | "pushd /sys/class/infiniband/ > /dev/null; for i in mlx5_*; "
18 | "do cat $i/ports/1/gid_attrs/types/* 2>/dev/null "
19 | "| grep v >/dev/null && echo $i ; done; popd > /dev/null"
20 | )
21 | os.environ["NCCL_IB_GID_INDEX"] = "3"
22 | os.environ["NCCL_IB_TC"] = "106"
23 |
24 |
25 | def configure_module(ulimit_value=8192):
26 | """
27 | Configure pytorch module environment. setting of ulimit and cv2 will be set.
28 |
29 | Args:
30 | ulimit_value(int): default open file number on linux. Default value: 8192.
31 | """
32 | # system setting
33 | try:
34 | import resource
35 |
36 | rlimit = resource.getrlimit(resource.RLIMIT_NOFILE)
37 | resource.setrlimit(resource.RLIMIT_NOFILE, (ulimit_value, rlimit[1]))
38 | except Exception:
39 | # Exception might be raised in Windows OS or rlimit reaches max limit number.
40 | # However, set rlimit value might not be necessary.
41 | pass
42 |
43 | # cv2
44 | # multiprocess might be harmful on performance of torch dataloader
45 | os.environ["OPENCV_OPENCL_RUNTIME"] = "disabled"
46 | try:
47 | cv2.setNumThreads(0)
48 | cv2.ocl.setUseOpenCL(False)
49 | except Exception:
50 | # cv2 version mismatch might rasie exceptions.
51 | pass
52 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # Copyright (c) Megvii, Inc. and its affiliates. All Rights Reserved
3 |
4 | import re
5 | import setuptools
6 | import glob
7 | from os import path
8 | import torch
9 | from torch.utils.cpp_extension import CppExtension
10 |
11 | torch_ver = [int(x) for x in torch.__version__.split(".")[:2]]
12 | assert torch_ver >= [1, 3], "Requires PyTorch >= 1.3"
13 |
14 |
15 | def get_extensions():
16 | this_dir = path.dirname(path.abspath(__file__))
17 | extensions_dir = path.join(this_dir, "yolox", "layers", "csrc")
18 |
19 | main_source = path.join(extensions_dir, "vision.cpp")
20 | sources = glob.glob(path.join(extensions_dir, "**", "*.cpp"))
21 |
22 | sources = [main_source] + sources
23 | extension = CppExtension
24 |
25 | extra_compile_args = {"cxx": ["-O3"]}
26 | define_macros = []
27 |
28 | include_dirs = [extensions_dir]
29 |
30 | ext_modules = [
31 | extension(
32 | "yolox._C",
33 | sources,
34 | include_dirs=include_dirs,
35 | define_macros=define_macros,
36 | extra_compile_args=extra_compile_args,
37 | )
38 | ]
39 |
40 | return ext_modules
41 |
42 |
43 | with open("yolox/__init__.py", "r") as f:
44 | version = re.search(
45 | r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]',
46 | f.read(), re.MULTILINE
47 | ).group(1)
48 |
49 |
50 | with open("README.md", "r") as f:
51 | long_description = f.read()
52 |
53 |
54 | setuptools.setup(
55 | name="yolox",
56 | version=version,
57 | author="basedet team",
58 | python_requires=">=3.6",
59 | long_description=long_description,
60 | ext_modules=get_extensions(),
61 | classifiers=["Programming Language :: Python :: 3", "Operating System :: OS Independent"],
62 | cmdclass={"build_ext": torch.utils.cpp_extension.BuildExtension},
63 | packages=setuptools.find_packages(),
64 | )
65 |
--------------------------------------------------------------------------------
/demo/MegEngine/python/convert_weights.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | import argparse
4 | from collections import OrderedDict
5 |
6 | import megengine as mge
7 | import torch
8 |
9 |
10 | def make_parser():
11 | parser = argparse.ArgumentParser()
12 | parser.add_argument("-w", "--weights", type=str, help="path of weight file")
13 | parser.add_argument(
14 | "-o",
15 | "--output",
16 | default="weight_mge.pkl",
17 | type=str,
18 | help="path of weight file",
19 | )
20 | return parser
21 |
22 |
23 | def numpy_weights(weight_file):
24 | torch_weights = torch.load(weight_file, map_location="cpu")
25 | if "model" in torch_weights:
26 | torch_weights = torch_weights["model"]
27 | new_dict = OrderedDict()
28 | for k, v in torch_weights.items():
29 | new_dict[k] = v.cpu().numpy()
30 | return new_dict
31 |
32 |
33 | def map_weights(weight_file, output_file):
34 | torch_weights = numpy_weights(weight_file)
35 |
36 | new_dict = OrderedDict()
37 | for k, v in torch_weights.items():
38 | if "num_batches_tracked" in k:
39 | print("drop: {}".format(k))
40 | continue
41 | if k.endswith("bias"):
42 | print("bias key: {}".format(k))
43 | v = v.reshape(1, -1, 1, 1)
44 | new_dict[k] = v
45 | elif "dconv" in k and "conv.weight" in k:
46 | print("depthwise conv key: {}".format(k))
47 | cout, cin, k1, k2 = v.shape
48 | v = v.reshape(cout, 1, cin, k1, k2)
49 | new_dict[k] = v
50 | else:
51 | new_dict[k] = v
52 |
53 | mge.save(new_dict, output_file)
54 | print("save weights to {}".format(output_file))
55 |
56 |
57 | def main():
58 | parser = make_parser()
59 | args = parser.parse_args()
60 | map_weights(args.weights, args.output)
61 |
62 |
63 | if __name__ == "__main__":
64 | main()
65 |
--------------------------------------------------------------------------------
/yolox/exp/base_exp.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import torch
6 | from torch.nn import Module
7 |
8 | from yolox.utils import LRScheduler
9 |
10 | import ast
11 | import pprint
12 | from abc import ABCMeta, abstractmethod
13 | from tabulate import tabulate
14 | from typing import Dict
15 |
16 |
17 | class BaseExp(metaclass=ABCMeta):
18 | """Basic class for any experiment."""
19 |
20 | def __init__(self):
21 | self.seed = None
22 | self.output_dir = "./YOLOX_outputs"
23 | self.print_interval = 100
24 | self.eval_interval = 10
25 |
26 | @abstractmethod
27 | def get_model(self) -> Module:
28 | pass
29 |
30 | @abstractmethod
31 | def get_data_loader(
32 | self, batch_size: int, is_distributed: bool
33 | ) -> Dict[str, torch.utils.data.DataLoader]:
34 | pass
35 |
36 | @abstractmethod
37 | def get_optimizer(self, batch_size: int) -> torch.optim.Optimizer:
38 | pass
39 |
40 | @abstractmethod
41 | def get_lr_scheduler(
42 | self, lr: float, iters_per_epoch: int, **kwargs
43 | ) -> LRScheduler:
44 | pass
45 |
46 | @abstractmethod
47 | def get_evaluator(self):
48 | pass
49 |
50 | @abstractmethod
51 | def eval(self, model, evaluator, weights):
52 | pass
53 |
54 | def __repr__(self):
55 | table_header = ["keys", "values"]
56 | exp_table = [
57 | (str(k), pprint.pformat(v))
58 | for k, v in vars(self).items()
59 | if not k.startswith("_")
60 | ]
61 | return tabulate(exp_table, headers=table_header, tablefmt="fancy_grid")
62 |
63 | def merge(self, cfg_list):
64 | assert len(cfg_list) % 2 == 0
65 | for k, v in zip(cfg_list[0::2], cfg_list[1::2]):
66 | # only update value with same key
67 | if hasattr(self, k):
68 | src_value = getattr(self, k)
69 | src_type = type(src_value)
70 | if src_value is not None and src_type != type(v):
71 | try:
72 | v = src_type(v)
73 | except Exception:
74 | v = ast.literal_eval(v)
75 | setattr(self, k, v)
76 |
--------------------------------------------------------------------------------
/demo/ncnn/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/tools/trt.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | from loguru import logger
6 |
7 | import tensorrt as trt
8 | import torch
9 | from torch2trt import torch2trt
10 |
11 | from yolox.exp import get_exp
12 |
13 | import argparse
14 | import os
15 | import shutil
16 |
17 |
18 | def make_parser():
19 | parser = argparse.ArgumentParser("YOLOX ncnn deploy")
20 | parser.add_argument("-expn", "--experiment-name", type=str, default=None)
21 | parser.add_argument("-n", "--name", type=str, default=None, help="model name")
22 |
23 | parser.add_argument(
24 | "-f",
25 | "--exp_file",
26 | default=None,
27 | type=str,
28 | help="pls input your expriment description file",
29 | )
30 | parser.add_argument("-c", "--ckpt", default=None, type=str, help="ckpt path")
31 | return parser
32 |
33 |
34 | @logger.catch
35 | def main():
36 | args = make_parser().parse_args()
37 | exp = get_exp(args.exp_file, args.name)
38 | if not args.experiment_name:
39 | args.experiment_name = exp.exp_name
40 |
41 | model = exp.get_model()
42 | file_name = os.path.join(exp.output_dir, args.experiment_name)
43 | os.makedirs(file_name, exist_ok=True)
44 | if args.ckpt is None:
45 | ckpt_file = os.path.join(file_name, "best_ckpt.pth.tar")
46 | else:
47 | ckpt_file = args.ckpt
48 |
49 | ckpt = torch.load(ckpt_file, map_location="cpu")
50 | # load the model state dict
51 |
52 | model.load_state_dict(ckpt["model"])
53 | logger.info("loaded checkpoint done.")
54 | model.eval()
55 | model.cuda()
56 | model.head.decode_in_inference = False
57 | x = torch.ones(1, 3, exp.test_size[0], exp.test_size[1]).cuda()
58 | model_trt = torch2trt(
59 | model,
60 | [x],
61 | fp16_mode=True,
62 | log_level=trt.Logger.INFO,
63 | max_workspace_size=(1 << 32),
64 | )
65 | torch.save(model_trt.state_dict(), os.path.join(file_name, "model_trt.pth"))
66 | logger.info("Converted TensorRT model done.")
67 | engine_file = os.path.join(file_name, "model_trt.engine")
68 | engine_file_demo = os.path.join("demo", "TensorRT", "cpp", "model_trt.engine")
69 | with open(engine_file, "wb") as f:
70 | f.write(model_trt.engine.serialize())
71 |
72 | shutil.copyfile(engine_file, engine_file_demo)
73 |
74 | logger.info("Converted TensorRT model engine file is saved for C++ inference.")
75 |
76 |
77 | if __name__ == "__main__":
78 | main()
79 |
--------------------------------------------------------------------------------
/demo/MegEngine/python/models/yolo_fpn.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- encoding: utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import megengine.functional as F
6 | import megengine.module as M
7 |
8 | from .darknet import Darknet
9 | from .network_blocks import BaseConv, UpSample
10 |
11 |
12 | class YOLOFPN(M.Module):
13 | """
14 | YOLOFPN module. Darknet 53 is the default backbone of this model.
15 | """
16 |
17 | def __init__(
18 | self, depth=53, in_features=["dark3", "dark4", "dark5"],
19 | ):
20 | super().__init__()
21 |
22 | self.backbone = Darknet(depth)
23 | self.in_features = in_features
24 |
25 | # out 1
26 | self.out1_cbl = self._make_cbl(512, 256, 1)
27 | self.out1 = self._make_embedding([256, 512], 512 + 256)
28 |
29 | # out 2
30 | self.out2_cbl = self._make_cbl(256, 128, 1)
31 | self.out2 = self._make_embedding([128, 256], 256 + 128)
32 |
33 | # upsample
34 | self.upsample = UpSample(scale_factor=2, mode="bilinear")
35 |
36 | def _make_cbl(self, _in, _out, ks):
37 | return BaseConv(_in, _out, ks, stride=1, act="lrelu")
38 |
39 | def _make_embedding(self, filters_list, in_filters):
40 | m = M.Sequential(
41 | *[
42 | self._make_cbl(in_filters, filters_list[0], 1),
43 | self._make_cbl(filters_list[0], filters_list[1], 3),
44 |
45 | self._make_cbl(filters_list[1], filters_list[0], 1),
46 |
47 | self._make_cbl(filters_list[0], filters_list[1], 3),
48 | self._make_cbl(filters_list[1], filters_list[0], 1),
49 | ]
50 | )
51 | return m
52 |
53 | def forward(self, inputs):
54 | """
55 | Args:
56 | inputs (Tensor): input image.
57 |
58 | Returns:
59 | Tuple[Tensor]: FPN output features..
60 | """
61 | # backbone
62 | out_features = self.backbone(inputs)
63 | x2, x1, x0 = [out_features[f] for f in self.in_features]
64 |
65 | # yolo branch 1
66 | x1_in = self.out1_cbl(x0)
67 | x1_in = self.upsample(x1_in)
68 | x1_in = F.concat([x1_in, x1], 1)
69 | out_dark4 = self.out1(x1_in)
70 |
71 | # yolo branch 2
72 | x2_in = self.out2_cbl(out_dark4)
73 | x2_in = self.upsample(x2_in)
74 | x2_in = F.concat([x2_in, x2], 1)
75 | out_dark3 = self.out2(x2_in)
76 |
77 | outputs = (out_dark3, out_dark4, x0)
78 | return outputs
79 |
--------------------------------------------------------------------------------
/yolox/data/data_prefetcher.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | import torch
6 | import torch.distributed as dist
7 |
8 | from yolox.utils import synchronize
9 |
10 | import random
11 |
12 |
13 | class DataPrefetcher:
14 | """
15 | DataPrefetcher is inspired by code of following file:
16 | https://github.com/NVIDIA/apex/blob/master/examples/imagenet/main_amp.py
17 | It could speedup your pytorch dataloader. For more information, please check
18 | https://github.com/NVIDIA/apex/issues/304#issuecomment-493562789.
19 | """
20 |
21 | def __init__(self, loader):
22 | self.loader = iter(loader)
23 | self.stream = torch.cuda.Stream()
24 | self.input_cuda = self._input_cuda_for_image
25 | self.record_stream = DataPrefetcher._record_stream_for_image
26 | self.preload()
27 |
28 | def preload(self):
29 | try:
30 | self.next_input, self.next_target, _, _ = next(self.loader)
31 | except StopIteration:
32 | self.next_input = None
33 | self.next_target = None
34 | return
35 |
36 | with torch.cuda.stream(self.stream):
37 | self.input_cuda()
38 | self.next_target = self.next_target.cuda(non_blocking=True)
39 |
40 | def next(self):
41 | torch.cuda.current_stream().wait_stream(self.stream)
42 | input = self.next_input
43 | target = self.next_target
44 | if input is not None:
45 | self.record_stream(input)
46 | if target is not None:
47 | target.record_stream(torch.cuda.current_stream())
48 | self.preload()
49 | return input, target
50 |
51 | def _input_cuda_for_image(self):
52 | self.next_input = self.next_input.cuda(non_blocking=True)
53 |
54 | @staticmethod
55 | def _record_stream_for_image(input):
56 | input.record_stream(torch.cuda.current_stream())
57 |
58 |
59 | def random_resize(data_loader, exp, epoch, rank, is_distributed):
60 | tensor = torch.LongTensor(1).cuda()
61 | if is_distributed:
62 | synchronize()
63 |
64 | if rank == 0:
65 | if epoch > exp.max_epoch - 10:
66 | size = exp.input_size
67 | else:
68 | size = random.randint(*exp.random_size)
69 | size = int(32 * size)
70 | tensor.fill_(size)
71 |
72 | if is_distributed:
73 | synchronize()
74 | dist.broadcast(tensor, 0)
75 |
76 | input_size = data_loader.change_input_dim(multiple=tensor.item(), random_range=None)
77 | return input_size
78 |
--------------------------------------------------------------------------------
/demo/MegEngine/cpp/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -e
3 |
4 | if [ -z $CXX ];then
5 | echo "please export you c++ toolchain to CXX"
6 | echo "for example:"
7 | echo "build for host: export CXX=g++"
8 | echo "cross build for aarch64-android(always locate in NDK): export CXX=aarch64-linux-android21-clang++"
9 | echo "cross build for aarch64-linux: export CXX=aarch64-linux-gnu-g++"
10 | exit -1
11 | fi
12 |
13 | if [ -z $MGE_INSTALL_PATH ];then
14 | echo "please refsi ./README.md to init MGE_INSTALL_PATH env"
15 | exit -1
16 | fi
17 |
18 | if [ -z $OPENCV_INSTALL_INCLUDE_PATH ];then
19 | echo "please refs ./README.md to init OPENCV_INSTALL_INCLUDE_PATH env"
20 | exit -1
21 | fi
22 |
23 | if [ -z $OPENCV_INSTALL_LIB_PATH ];then
24 | echo "please refs ./README.md to init OPENCV_INSTALL_LIB_PATH env"
25 | exit -1
26 | fi
27 |
28 | INCLUDE_FLAG="-I$MGE_INSTALL_PATH/include -I$OPENCV_INSTALL_INCLUDE_PATH"
29 | LINK_FLAG="-L$MGE_INSTALL_PATH/lib/ -lmegengine -L$OPENCV_INSTALL_LIB_PATH -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_imgcodecs"
30 | BUILD_FLAG="-static-libstdc++ -O3 -pie -fPIE -g"
31 |
32 | if [[ $CXX =~ "android" ]]; then
33 | LINK_FLAG="${LINK_FLAG} -llog -lz"
34 | fi
35 |
36 | echo "CXX: $CXX"
37 | echo "MGE_INSTALL_PATH: $MGE_INSTALL_PATH"
38 | echo "INCLUDE_FLAG: $INCLUDE_FLAG"
39 | echo "LINK_FLAG: $LINK_FLAG"
40 | echo "BUILD_FLAG: $BUILD_FLAG"
41 |
42 | echo "[" > compile_commands.json
43 | echo "{" >> compile_commands.json
44 | echo "\"directory\": \"$PWD\"," >> compile_commands.json
45 | echo "\"command\": \"$CXX yolox.cpp -o yolox ${INCLUDE_FLAG} ${LINK_FLAG}\"," >> compile_commands.json
46 | echo "\"file\": \"$PWD/yolox.cpp\"," >> compile_commands.json
47 | echo "}," >> compile_commands.json
48 | echo "]" >> compile_commands.json
49 | $CXX yolox.cpp -o yolox ${INCLUDE_FLAG} ${LINK_FLAG} ${BUILD_FLAG}
50 |
51 | echo "build success, output file: yolox"
52 | if [[ $CXX =~ "android" ]]; then
53 | echo "try command to run:"
54 | echo "adb push/scp $MGE_INSTALL_PATH/lib/libmegengine.so android_phone"
55 | echo "adb push/scp $OPENCV_INSTALL_LIB_PATH/*.so android_phone"
56 | echo "adb push/scp ./yolox yolox_s.mge android_phone"
57 | echo "adb push/scp ../../../assets/dog.jpg android_phone"
58 | echo "adb/ssh to android_phone, then run: LD_LIBRARY_PATH=. ./yolox yolox_s.mge dog.jpg cpu/multithread "
59 | else
60 | echo "try command to run: LD_LIBRARY_PATH=$MGE_INSTALL_PATH/lib/:$OPENCV_INSTALL_LIB_PATH ./yolox yolox_s.mge ../../../assets/dog.jpg cuda/cpu/multithread "
61 | fi
62 |
--------------------------------------------------------------------------------
/yolox/models/yolo_fpn.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- encoding: utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import torch
6 | import torch.nn as nn
7 |
8 | from .darknet import Darknet
9 | from .network_blocks import BaseConv
10 |
11 |
12 | class YOLOFPN(nn.Module):
13 | """
14 | YOLOFPN module. Darknet 53 is the default backbone of this model.
15 | """
16 |
17 | def __init__(
18 | self,
19 | depth=53,
20 | in_features=["dark3", "dark4", "dark5"],
21 | ):
22 | super().__init__()
23 |
24 | self.backbone = Darknet(depth)
25 | self.in_features = in_features
26 |
27 | # out 1
28 | self.out1_cbl = self._make_cbl(512, 256, 1)
29 | self.out1 = self._make_embedding([256, 512], 512 + 256)
30 |
31 | # out 2
32 | self.out2_cbl = self._make_cbl(256, 128, 1)
33 | self.out2 = self._make_embedding([128, 256], 256 + 128)
34 |
35 | # upsample
36 | self.upsample = nn.Upsample(scale_factor=2, mode="nearest")
37 |
38 | def _make_cbl(self, _in, _out, ks):
39 | return BaseConv(_in, _out, ks, stride=1, act="lrelu")
40 |
41 | def _make_embedding(self, filters_list, in_filters):
42 | m = nn.Sequential(
43 | *[
44 | self._make_cbl(in_filters, filters_list[0], 1),
45 | self._make_cbl(filters_list[0], filters_list[1], 3),
46 | self._make_cbl(filters_list[1], filters_list[0], 1),
47 | self._make_cbl(filters_list[0], filters_list[1], 3),
48 | self._make_cbl(filters_list[1], filters_list[0], 1),
49 | ]
50 | )
51 | return m
52 |
53 | def load_pretrained_model(self, filename="./weights/darknet53.mix.pth"):
54 | with open(filename, "rb") as f:
55 | state_dict = torch.load(f, map_location="cpu")
56 | print("loading pretrained weights...")
57 | self.backbone.load_state_dict(state_dict)
58 |
59 | def forward(self, inputs):
60 | """
61 | Args:
62 | inputs (Tensor): input image.
63 |
64 | Returns:
65 | Tuple[Tensor]: FPN output features..
66 | """
67 | # backbone
68 | out_features = self.backbone(inputs)
69 | x2, x1, x0 = [out_features[f] for f in self.in_features]
70 |
71 | # yolo branch 1
72 | x1_in = self.out1_cbl(x0)
73 | x1_in = self.upsample(x1_in)
74 | x1_in = torch.cat([x1_in, x1], 1)
75 | out_dark4 = self.out1(x1_in)
76 |
77 | # yolo branch 2
78 | x2_in = self.out2_cbl(out_dark4)
79 | x2_in = self.upsample(x2_in)
80 | x2_in = torch.cat([x2_in, x2], 1)
81 | out_dark3 = self.out2(x2_in)
82 |
83 | outputs = (out_dark3, out_dark4, x0)
84 | return outputs
85 |
--------------------------------------------------------------------------------
/yolox/utils/ema.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 | import torch
5 | import torch.nn as nn
6 |
7 | import math
8 | from copy import deepcopy
9 |
10 |
11 | def is_parallel(model):
12 | """check if model is in parallel mode."""
13 | import apex
14 |
15 | parallel_type = (
16 | nn.parallel.DataParallel,
17 | nn.parallel.DistributedDataParallel,
18 | apex.parallel.distributed.DistributedDataParallel,
19 | )
20 | return isinstance(model, parallel_type)
21 |
22 |
23 | def copy_attr(a, b, include=(), exclude=()):
24 | # Copy attributes from b to a, options to only include [...] and to exclude [...]
25 | for k, v in b.__dict__.items():
26 | if (len(include) and k not in include) or k.startswith("_") or k in exclude:
27 | continue
28 | else:
29 | setattr(a, k, v)
30 |
31 |
32 | class ModelEMA:
33 | """
34 | Model Exponential Moving Average from https://github.com/rwightman/pytorch-image-models
35 | Keep a moving average of everything in the model state_dict (parameters and buffers).
36 | This is intended to allow functionality like
37 | https://www.tensorflow.org/api_docs/python/tf/train/ExponentialMovingAverage
38 | A smoothed version of the weights is necessary for some training schemes to perform well.
39 | This class is sensitive where it is initialized in the sequence of model init,
40 | GPU assignment and distributed training wrappers.
41 | """
42 |
43 | def __init__(self, model, decay=0.9999, updates=0):
44 | """
45 | Args:
46 | model (nn.Module): model to apply EMA.
47 | decay (float): ema decay reate.
48 | updates (int): counter of EMA updates.
49 | """
50 | # Create EMA(FP32)
51 | self.ema = deepcopy(model.module if is_parallel(model) else model).eval()
52 | self.updates = updates
53 | # decay exponential ramp (to help early epochs)
54 | self.decay = lambda x: decay * (1 - math.exp(-x / 2000))
55 | for p in self.ema.parameters():
56 | p.requires_grad_(False)
57 |
58 | def update(self, model):
59 | # Update EMA parameters
60 | with torch.no_grad():
61 | self.updates += 1
62 | d = self.decay(self.updates)
63 |
64 | msd = (
65 | model.module.state_dict() if is_parallel(model) else model.state_dict()
66 | ) # model state_dict
67 | for k, v in self.ema.state_dict().items():
68 | if v.dtype.is_floating_point:
69 | v *= d
70 | v += (1.0 - d) * msd[k].detach()
71 |
72 | def update_attr(self, model, include=(), exclude=("process_group", "reducer")):
73 | # Update EMA attributes
74 | copy_attr(self.ema, model, include, exclude)
75 |
--------------------------------------------------------------------------------
/demo/MegEngine/python/process.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import cv2
6 | import megengine.functional as F
7 | import numpy as np
8 |
9 | __all__ = [
10 | "preprocess",
11 | "postprocess",
12 | ]
13 |
14 |
15 | def preprocess(image, input_size, mean, std, swap=(2, 0, 1)):
16 | if len(image.shape) == 3:
17 | padded_img = np.ones((input_size[0], input_size[1], 3)) * 114.0
18 | else:
19 | padded_img = np.ones(input_size) * 114.0
20 | img = np.array(image)
21 | r = min(input_size[0] / img.shape[0], input_size[1] / img.shape[1])
22 | resized_img = cv2.resize(
23 | img,
24 | (int(img.shape[1] * r), int(img.shape[0] * r)),
25 | interpolation=cv2.INTER_LINEAR,
26 | ).astype(np.float32)
27 | padded_img[: int(img.shape[0] * r), : int(img.shape[1] * r)] = resized_img
28 | image = padded_img
29 |
30 | image = image.astype(np.float32)
31 | image = image[:, :, ::-1]
32 | image /= 255.0
33 | if mean is not None:
34 | image -= mean
35 | if std is not None:
36 | image /= std
37 | image = image.transpose(swap)
38 | image = np.ascontiguousarray(image, dtype=np.float32)
39 | return image, r
40 |
41 |
42 | def postprocess(prediction, num_classes, conf_thre=0.7, nms_thre=0.45):
43 | box_corner = F.zeros_like(prediction)
44 | box_corner[:, :, 0] = prediction[:, :, 0] - prediction[:, :, 2] / 2
45 | box_corner[:, :, 1] = prediction[:, :, 1] - prediction[:, :, 3] / 2
46 | box_corner[:, :, 2] = prediction[:, :, 0] + prediction[:, :, 2] / 2
47 | box_corner[:, :, 3] = prediction[:, :, 1] + prediction[:, :, 3] / 2
48 | prediction[:, :, :4] = box_corner[:, :, :4]
49 |
50 | output = [None for _ in range(len(prediction))]
51 | for i, image_pred in enumerate(prediction):
52 |
53 | # If none are remaining => process next image
54 | if not image_pred.shape[0]:
55 | continue
56 | # Get score and class with highest confidence
57 | class_conf = F.max(image_pred[:, 5 : 5 + num_classes], 1, keepdims=True)
58 | class_pred = F.argmax(image_pred[:, 5 : 5 + num_classes], 1, keepdims=True)
59 |
60 | class_conf_squeeze = F.squeeze(class_conf)
61 | conf_mask = image_pred[:, 4] * class_conf_squeeze >= conf_thre
62 | detections = F.concat((image_pred[:, :5], class_conf, class_pred), 1)
63 | detections = detections[conf_mask]
64 | if not detections.shape[0]:
65 | continue
66 |
67 | nms_out_index = F.vision.nms(
68 | detections[:, :4], detections[:, 4] * detections[:, 5], nms_thre,
69 | )
70 | detections = detections[nms_out_index]
71 | if output[i] is None:
72 | output[i] = detections
73 | else:
74 | output[i] = F.concat((output[i], detections))
75 |
76 | return output
77 |
--------------------------------------------------------------------------------
/yolox/models/losses.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- encoding: utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import torch
6 | import torch.nn as nn
7 | import numpy as np
8 |
9 | class IOUloss(nn.Module):
10 | def __init__(self, reduction="none", loss_type="iou"):
11 | super(IOUloss, self).__init__()
12 | self.reduction = reduction
13 | self.loss_type = loss_type
14 |
15 | def forward(self, pred, target):
16 | assert pred.shape[0] == target.shape[0]
17 |
18 | pred = pred.view(-1, 4)
19 | target = target.view(-1, 4)
20 | tl = torch.max(
21 | (pred[:, :2] - pred[:, 2:] / 2), (target[:, :2] - target[:, 2:] / 2)
22 | )
23 | br = torch.min(
24 | (pred[:, :2] + pred[:, 2:] / 2), (target[:, :2] + target[:, 2:] / 2)
25 | )
26 |
27 | area_p = torch.prod(pred[:, 2:], 1)
28 | area_g = torch.prod(target[:, 2:], 1)
29 |
30 | en = (tl < br).type(tl.type()).prod(dim=1)
31 | area_i = torch.prod(br - tl, 1) * en
32 | iou = (area_i) / (area_p + area_g - area_i + 1e-16)
33 |
34 | if self.loss_type == "iou":
35 | loss = 1 - iou ** 2
36 | elif self.loss_type == "giou":
37 | c_tl = torch.min(
38 | (pred[:, :2] - pred[:, 2:] / 2), (target[:, :2] - target[:, 2:] / 2)
39 | )
40 | c_br = torch.max(
41 | (pred[:, :2] + pred[:, 2:] / 2), (target[:, :2] + target[:, 2:] / 2)
42 | )
43 | area_c = torch.prod(c_br - c_tl, 1)
44 | giou = iou - (area_c - area_i) / area_c.clamp(1e-16)
45 | loss = 1 - giou.clamp(min=-1.0, max=1.0)
46 |
47 | if self.reduction == "mean":
48 | loss = loss.mean()
49 | elif self.reduction == "sum":
50 | loss = loss.sum()
51 |
52 | return loss
53 |
54 |
55 | class WingLoss(nn.Module):
56 | def __init__(self, w=10, e=2):
57 | super(WingLoss, self).__init__()
58 | # https://arxiv.org/pdf/1711.06753v4.pdf Figure 5
59 | self.w = w
60 | self.e = e
61 | self.C = self.w - self.w * np.log(1 + self.w / self.e)
62 |
63 | def forward(self, x, t, sigma=1):
64 | weight = torch.ones_like(t)
65 | weight[torch.where(t==-1)] = 0
66 | diff = weight * (x - t)
67 | abs_diff = diff.abs()
68 | flag = (abs_diff.data < self.w).float()
69 | y = flag * self.w * torch.log(1 + abs_diff / self.e) + (1 - flag) * (abs_diff - self.C)
70 | return y.sum()
71 |
72 |
73 | class LandmarksLoss(nn.Module):
74 | # BCEwithLogitLoss() with reduced missing label effects.
75 | def __init__(self, alpha=1.0):
76 | super(LandmarksLoss, self).__init__()
77 | #self.loss_fcn = WingLoss()#nn.SmoothL1Loss(reduction='sum')
78 | self.loss_fcn = nn.SmoothL1Loss(reduction='sum')
79 | self.alpha = alpha
80 |
81 | def forward(self, pred, truel):
82 | mask = truel > 0
83 | loss = self.loss_fcn(pred*mask, truel*mask)
84 | return loss / (torch.sum(mask) + 10e-14)
85 |
--------------------------------------------------------------------------------
/yolox/utils/logger.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | from loguru import logger
6 |
7 | import inspect
8 | import os
9 | import sys
10 |
11 |
12 | def get_caller_name(depth=0):
13 | """
14 | Args:
15 | depth (int): Depth of caller conext, use 0 for caller depth. Default value: 0.
16 |
17 | Returns:
18 | str: module name of the caller
19 | """
20 | # the following logic is a little bit faster than inspect.stack() logic
21 | frame = inspect.currentframe().f_back
22 | for _ in range(depth):
23 | frame = frame.f_back
24 |
25 | return frame.f_globals["__name__"]
26 |
27 |
28 | class StreamToLoguru:
29 | """
30 | stream object that redirects writes to a logger instance.
31 | """
32 |
33 | def __init__(self, level="INFO", caller_names=("apex", "pycocotools")):
34 | """
35 | Args:
36 | level(str): log level string of loguru. Default value: "INFO".
37 | caller_names(tuple): caller names of redirected module.
38 | Default value: (apex, pycocotools).
39 | """
40 | self.level = level
41 | self.linebuf = ""
42 | self.caller_names = caller_names
43 |
44 | def write(self, buf):
45 | full_name = get_caller_name(depth=1)
46 | module_name = full_name.rsplit(".", maxsplit=-1)[0]
47 | if module_name in self.caller_names:
48 | for line in buf.rstrip().splitlines():
49 | # use caller level log
50 | logger.opt(depth=2).log(self.level, line.rstrip())
51 | else:
52 | sys.__stdout__.write(buf)
53 |
54 | def flush(self):
55 | pass
56 |
57 |
58 | def redirect_sys_output(log_level="INFO"):
59 | redirect_logger = StreamToLoguru(log_level)
60 | sys.stderr = redirect_logger
61 | sys.stdout = redirect_logger
62 |
63 |
64 | def setup_logger(save_dir, distributed_rank=0, filename="log.txt", mode="a"):
65 | """setup logger for training and testing.
66 | Args:
67 | save_dir(str): location to save log file
68 | distributed_rank(int): device rank when multi-gpu environment
69 | filename (string): log save name.
70 | mode(str): log file write mode, `append` or `override`. default is `a`.
71 |
72 | Return:
73 | logger instance.
74 | """
75 | loguru_format = (
76 | "{time:YYYY-MM-DD HH:mm:ss} | "
77 | "{level: <8} | "
78 | "{name}:{line} - {message}"
79 | )
80 |
81 | logger.remove()
82 | save_file = os.path.join(save_dir, filename)
83 | if mode == "o" and os.path.exists(save_file):
84 | os.remove(save_file)
85 | # only keep logger in rank0 process
86 | if distributed_rank == 0:
87 | logger.add(
88 | sys.stderr,
89 | format=loguru_format,
90 | level="INFO",
91 | enqueue=True,
92 | )
93 | logger.add(save_file)
94 |
95 | # redirect stdout/stderr to loguru
96 | redirect_sys_output("INFO")
97 |
--------------------------------------------------------------------------------
/demo/ONNXRuntime/onnx_inference.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | import argparse
6 | import os
7 |
8 | import cv2
9 | import numpy as np
10 |
11 | import onnxruntime
12 |
13 | from yolox.data.data_augment import preproc as preprocess
14 | from yolox.data.datasets import COCO_CLASSES
15 | from yolox.utils import mkdir, multiclass_nms, demo_postprocess, vis
16 |
17 |
18 | def make_parser():
19 | parser = argparse.ArgumentParser("onnxruntime inference sample")
20 | parser.add_argument(
21 | "-m",
22 | "--model",
23 | type=str,
24 | default="yolox.onnx",
25 | help="Input your onnx model.",
26 | )
27 | parser.add_argument(
28 | "-i",
29 | "--image_path",
30 | type=str,
31 | default='test_image.png',
32 | help="Path to your input image.",
33 | )
34 | parser.add_argument(
35 | "-o",
36 | "--output_dir",
37 | type=str,
38 | default='demo_output',
39 | help="Path to your output directory.",
40 | )
41 | parser.add_argument(
42 | "-s",
43 | "--score_thr",
44 | type=float,
45 | default=0.3,
46 | help="Score threshould to filter the result.",
47 | )
48 | parser.add_argument(
49 | "--input_shape",
50 | type=str,
51 | default="640,640",
52 | help="Specify an input shape for inference.",
53 | )
54 | parser.add_argument(
55 | "--with_p6",
56 | action="store_true",
57 | help="Whether your model uses p6 in FPN/PAN.",
58 | )
59 | return parser
60 |
61 |
62 | if __name__ == '__main__':
63 | args = make_parser().parse_args()
64 |
65 | input_shape = tuple(map(int, args.input_shape.split(',')))
66 | origin_img = cv2.imread(args.image_path)
67 | mean = (0.485, 0.456, 0.406)
68 | std = (0.229, 0.224, 0.225)
69 | img, ratio = preprocess(origin_img, input_shape, mean, std)
70 |
71 | session = onnxruntime.InferenceSession(args.model)
72 |
73 | ort_inputs = {session.get_inputs()[0].name: img[None, :, :, :]}
74 | output = session.run(None, ort_inputs)
75 | predictions = demo_postprocess(output[0], input_shape, p6=args.with_p6)[0]
76 |
77 | boxes = predictions[:, :4]
78 | scores = predictions[:, 4:5] * predictions[:, 5:]
79 |
80 | boxes_xyxy = np.ones_like(boxes)
81 | boxes_xyxy[:, 0] = boxes[:, 0] - boxes[:, 2]/2.
82 | boxes_xyxy[:, 1] = boxes[:, 1] - boxes[:, 3]/2.
83 | boxes_xyxy[:, 2] = boxes[:, 0] + boxes[:, 2]/2.
84 | boxes_xyxy[:, 3] = boxes[:, 1] + boxes[:, 3]/2.
85 | boxes_xyxy /= ratio
86 | dets = multiclass_nms(boxes_xyxy, scores, nms_thr=0.45, score_thr=0.1)
87 | if dets is not None:
88 | final_boxes, final_scores, final_cls_inds = dets[:, :4], dets[:, 4], dets[:, 5]
89 | origin_img = vis(origin_img, final_boxes, final_scores, final_cls_inds,
90 | conf=args.score_thr, class_names=COCO_CLASSES)
91 |
92 | mkdir(args.output_dir)
93 | output_path = os.path.join(args.output_dir, args.image_path.split("/")[-1])
94 | cv2.imwrite(output_path, origin_img)
95 |
--------------------------------------------------------------------------------
/yolox/utils/demo_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import numpy as np
6 |
7 | import os
8 |
9 | __all__ = ["mkdir", "nms", "multiclass_nms", "demo_postprocess"]
10 |
11 |
12 | def mkdir(path):
13 | if not os.path.exists(path):
14 | os.makedirs(path)
15 |
16 |
17 | def nms(boxes, scores, nms_thr):
18 | """Single class NMS implemented in Numpy."""
19 | x1 = boxes[:, 0]
20 | y1 = boxes[:, 1]
21 | x2 = boxes[:, 2]
22 | y2 = boxes[:, 3]
23 |
24 | areas = (x2 - x1 + 1) * (y2 - y1 + 1)
25 | order = scores.argsort()[::-1]
26 |
27 | keep = []
28 | while order.size > 0:
29 | i = order[0]
30 | keep.append(i)
31 | xx1 = np.maximum(x1[i], x1[order[1:]])
32 | yy1 = np.maximum(y1[i], y1[order[1:]])
33 | xx2 = np.minimum(x2[i], x2[order[1:]])
34 | yy2 = np.minimum(y2[i], y2[order[1:]])
35 |
36 | w = np.maximum(0.0, xx2 - xx1 + 1)
37 | h = np.maximum(0.0, yy2 - yy1 + 1)
38 | inter = w * h
39 | ovr = inter / (areas[i] + areas[order[1:]] - inter)
40 |
41 | inds = np.where(ovr <= nms_thr)[0]
42 | order = order[inds + 1]
43 |
44 | return keep
45 |
46 |
47 | def multiclass_nms(boxes, scores, nms_thr, score_thr):
48 | """Multiclass NMS implemented in Numpy"""
49 | final_dets = []
50 | num_classes = scores.shape[1]
51 | for cls_ind in range(num_classes):
52 | cls_scores = scores[:, cls_ind]
53 | valid_score_mask = cls_scores > score_thr
54 | if valid_score_mask.sum() == 0:
55 | continue
56 | else:
57 | valid_scores = cls_scores[valid_score_mask]
58 | valid_boxes = boxes[valid_score_mask]
59 | keep = nms(valid_boxes, valid_scores, nms_thr)
60 | if len(keep) > 0:
61 | cls_inds = np.ones((len(keep), 1)) * cls_ind
62 | dets = np.concatenate(
63 | [valid_boxes[keep], valid_scores[keep, None], cls_inds], 1
64 | )
65 | final_dets.append(dets)
66 | if len(final_dets) == 0:
67 | return None
68 | return np.concatenate(final_dets, 0)
69 |
70 |
71 | def demo_postprocess(outputs, img_size, p6=False):
72 |
73 | grids = []
74 | expanded_strides = []
75 |
76 | if not p6:
77 | strides = [8, 16, 32]
78 | else:
79 | strides = [8, 16, 32, 64]
80 |
81 | hsizes = [img_size[0] // stride for stride in strides]
82 | wsizes = [img_size[1] // stride for stride in strides]
83 |
84 | for hsize, wsize, stride in zip(hsizes, wsizes, strides):
85 | xv, yv = np.meshgrid(np.arange(wsize), np.arange(hsize))
86 | grid = np.stack((xv, yv), 2).reshape(1, -1, 2)
87 | grids.append(grid)
88 | shape = grid.shape[:2]
89 | expanded_strides.append(np.full((*shape, 1), stride))
90 |
91 | grids = np.concatenate(grids, 1)
92 | expanded_strides = np.concatenate(expanded_strides, 1)
93 | outputs[..., :2] = (outputs[..., :2] + grids) * expanded_strides
94 | outputs[..., 2:4] = np.exp(outputs[..., 2:4]) * expanded_strides
95 |
96 | return outputs
97 |
--------------------------------------------------------------------------------
/exps/default/yolov3.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | import os
6 | import torch
7 | import torch.nn as nn
8 |
9 | from yolox.exp import Exp as MyExp
10 |
11 |
12 | class Exp(MyExp):
13 | def __init__(self):
14 | super(Exp, self).__init__()
15 | self.depth = 1.0
16 | self.width = 1.0
17 | self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]
18 |
19 | def get_model(self, sublinear=False):
20 | def init_yolo(M):
21 | for m in M.modules():
22 | if isinstance(m, nn.BatchNorm2d):
23 | m.eps = 1e-3
24 | m.momentum = 0.03
25 | if "model" not in self.__dict__:
26 | from yolox.models import YOLOX, YOLOFPN, YOLOXHead
27 | backbone = YOLOFPN()
28 | head = YOLOXHead(self.num_classes, self.width, in_channels=[128, 256, 512], act="lrelu")
29 | self.model = YOLOX(backbone, head)
30 | self.model.apply(init_yolo)
31 | self.model.head.initialize_biases(1e-2)
32 |
33 | return self.model
34 |
35 | def get_data_loader(self, batch_size, is_distributed, no_aug=False):
36 | from data.datasets.cocodataset import COCODataset
37 | from data.datasets.mosaicdetection import MosaicDetection
38 | from data.datasets.data_augment import TrainTransform
39 | from data.datasets.dataloading import YoloBatchSampler, DataLoader, InfiniteSampler
40 | import torch.distributed as dist
41 |
42 | dataset = COCODataset(
43 | data_dir='data/COCO/',
44 | json_file=self.train_ann,
45 | img_size=self.input_size,
46 | preproc=TrainTransform(
47 | rgb_means=(0.485, 0.456, 0.406),
48 | std=(0.229, 0.224, 0.225),
49 | max_labels=50
50 | ),
51 | )
52 |
53 | dataset = MosaicDetection(
54 | dataset,
55 | mosaic=not no_aug,
56 | img_size=self.input_size,
57 | preproc=TrainTransform(
58 | rgb_means=(0.485, 0.456, 0.406),
59 | std=(0.229, 0.224, 0.225),
60 | max_labels=120
61 | ),
62 | degrees=self.degrees,
63 | translate=self.translate,
64 | scale=self.scale,
65 | shear=self.shear,
66 | perspective=self.perspective,
67 | )
68 |
69 | self.dataset = dataset
70 |
71 | if is_distributed:
72 | batch_size = batch_size // dist.get_world_size()
73 | sampler = InfiniteSampler(len(self.dataset), seed=self.seed if self.seed else 0)
74 | else:
75 | sampler = torch.utils.data.RandomSampler(self.dataset)
76 |
77 | batch_sampler = YoloBatchSampler(
78 | sampler=sampler,
79 | batch_size=batch_size,
80 | drop_last=False,
81 | input_dimension=self.input_size,
82 | mosaic=not no_aug
83 | )
84 |
85 | dataloader_kwargs = {"num_workers": self.data_num_workers, "pin_memory": True}
86 | dataloader_kwargs["batch_sampler"] = batch_sampler
87 | train_loader = DataLoader(self.dataset, **dataloader_kwargs)
88 |
89 | return train_loader
90 |
--------------------------------------------------------------------------------
/yolox/utils/allreduce_norm.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import torch
6 | from torch import distributed as dist
7 | from torch import nn
8 |
9 | import pickle
10 | from collections import OrderedDict
11 |
12 | from .dist import _get_global_gloo_group, get_world_size
13 |
14 | ASYNC_NORM = (
15 | nn.BatchNorm1d,
16 | nn.BatchNorm2d,
17 | nn.BatchNorm3d,
18 | nn.InstanceNorm1d,
19 | nn.InstanceNorm2d,
20 | nn.InstanceNorm3d,
21 | )
22 |
23 | __all__ = [
24 | "get_async_norm_states",
25 | "pyobj2tensor",
26 | "tensor2pyobj",
27 | "all_reduce",
28 | "all_reduce_norm",
29 | ]
30 |
31 |
32 | def get_async_norm_states(module):
33 | async_norm_states = OrderedDict()
34 | for name, child in module.named_modules():
35 | if isinstance(child, ASYNC_NORM):
36 | for k, v in child.state_dict().items():
37 | async_norm_states[".".join([name, k])] = v
38 | return async_norm_states
39 |
40 |
41 | def pyobj2tensor(pyobj, device="cuda"):
42 | """serialize picklable python object to tensor"""
43 | storage = torch.ByteStorage.from_buffer(pickle.dumps(pyobj))
44 | return torch.ByteTensor(storage).to(device=device)
45 |
46 |
47 | def tensor2pyobj(tensor):
48 | """deserialize tensor to picklable python object"""
49 | return pickle.loads(tensor.cpu().numpy().tobytes())
50 |
51 |
52 | def _get_reduce_op(op_name):
53 | return {
54 | "sum": dist.ReduceOp.SUM,
55 | "mean": dist.ReduceOp.SUM,
56 | }[op_name.lower()]
57 |
58 |
59 | def all_reduce(py_dict, op="sum", group=None):
60 | """
61 | Apply all reduce function for python dict object.
62 | NOTE: make sure that every py_dict has the same keys and values are in the same shape.
63 |
64 | Args:
65 | py_dict (dict): dict to apply all reduce op.
66 | op (str): operator, could be "sum" or "mean".
67 | """
68 | world_size = get_world_size()
69 | if world_size == 1:
70 | return py_dict
71 | if group is None:
72 | group = _get_global_gloo_group()
73 | if dist.get_world_size(group) == 1:
74 | return py_dict
75 |
76 | # all reduce logic across different devices.
77 | py_key = list(py_dict.keys())
78 | py_key_tensor = pyobj2tensor(py_key)
79 | dist.broadcast(py_key_tensor, src=0)
80 | py_key = tensor2pyobj(py_key_tensor)
81 |
82 | tensor_shapes = [py_dict[k].shape for k in py_key]
83 | tensor_numels = [py_dict[k].numel() for k in py_key]
84 |
85 | flatten_tensor = torch.cat([py_dict[k].flatten() for k in py_key])
86 | dist.all_reduce(flatten_tensor, op=_get_reduce_op(op))
87 | if op == "mean":
88 | flatten_tensor /= world_size
89 |
90 | split_tensors = [
91 | x.reshape(shape)
92 | for x, shape in zip(torch.split(flatten_tensor, tensor_numels), tensor_shapes)
93 | ]
94 | return OrderedDict({k: v for k, v in zip(py_key, split_tensors)})
95 |
96 |
97 | def all_reduce_norm(module):
98 | """
99 | All reduce norm statistics in different devices.
100 | """
101 | states = get_async_norm_states(module)
102 | states = all_reduce(states, op="mean")
103 | module.load_state_dict(states, strict=False)
104 |
--------------------------------------------------------------------------------
/demo/ncnn/cpp/README.md:
--------------------------------------------------------------------------------
1 | # YOLOX-CPP-ncnn
2 |
3 | Cpp file compile of YOLOX object detection base on [ncnn](https://github.com/Tencent/ncnn).
4 |
5 | ## Tutorial
6 |
7 | ### Step1
8 | Clone [ncnn](https://github.com/Tencent/ncnn) first, then please following [build tutorial of ncnn](https://github.com/Tencent/ncnn/wiki/how-to-build) to build on your own device.
9 |
10 | ### Step2
11 | Use provided tools to generate onnx file.
12 | For example, if you want to generate onnx file of yolox-s, please run the following command:
13 | ```shell
14 | cd
15 | python3 tools/export_onnx.py -n yolox-s
16 | ```
17 | Then, a yolox.onnx file is generated.
18 |
19 | ### Step3
20 | Generate ncnn param and bin file.
21 | ```shell
22 | cd
23 | cd build/tools/ncnn
24 | ./onnx2ncnn yolox.onnx yolox.param yolox.bin
25 | ```
26 |
27 | Since Focus module is not supported in ncnn. Warnings like:
28 | ```shell
29 | Unsupported slice step !
30 | ```
31 | will be printed. However, don't worry! C++ version of Focus layer is already implemented in yolox.cpp.
32 |
33 | ### Step4
34 | Open **yolox.param**, and modify it.
35 | Before (just an example):
36 | ```
37 | 295 328
38 | Input images 0 1 images
39 | Split splitncnn_input0 1 4 images images_splitncnn_0 images_splitncnn_1 images_splitncnn_2 images_splitncnn_3
40 | Crop Slice_4 1 1 images_splitncnn_3 647 -23309=1,0 -23310=1,2147483647 -23311=1,1
41 | Crop Slice_9 1 1 647 652 -23309=1,0 -23310=1,2147483647 -23311=1,2
42 | Crop Slice_14 1 1 images_splitncnn_2 657 -23309=1,0 -23310=1,2147483647 -23311=1,1
43 | Crop Slice_19 1 1 657 662 -23309=1,1 -23310=1,2147483647 -23311=1,2
44 | Crop Slice_24 1 1 images_splitncnn_1 667 -23309=1,1 -23310=1,2147483647 -23311=1,1
45 | Crop Slice_29 1 1 667 672 -23309=1,0 -23310=1,2147483647 -23311=1,2
46 | Crop Slice_34 1 1 images_splitncnn_0 677 -23309=1,1 -23310=1,2147483647 -23311=1,1
47 | Crop Slice_39 1 1 677 682 -23309=1,1 -23310=1,2147483647 -23311=1,2
48 | Concat Concat_40 4 1 652 672 662 682 683 0=0
49 | ...
50 | ```
51 | * Change first number for 295 to 295 - 9 = 286(since we will remove 10 layers and add 1 layers, total layers number should minus 9).
52 | * Then remove 10 lines of code from Split to Concat, but remember the last but 2nd number: 683.
53 | * Add YoloV5Focus layer After Input (using previous number 683):
54 | ```
55 | YoloV5Focus focus 1 1 images 683
56 | ```
57 | After(just an exmaple):
58 | ```
59 | 286 328
60 | Input images 0 1 images
61 | YoloV5Focus focus 1 1 images 683
62 | ...
63 | ```
64 |
65 | ### Step5
66 | Use ncnn_optimize to generate new param and bin:
67 | ```shell
68 | # suppose you are still under ncnn/build/tools/ncnn dir.
69 | ../ncnnoptimize model.param model.bin yolox.param yolox.bin 65536
70 | ```
71 |
72 | ### Step6
73 | Copy or Move yolox.cpp file into ncnn/examples, modify the CMakeList.txt, then build yolox
74 |
75 | ### Step7
76 | Inference image with executable file yolox, enjoy the detect result:
77 | ```shell
78 | ./yolox demo.jpg
79 | ```
80 |
81 | ## Acknowledgement
82 |
83 | * [ncnn](https://github.com/Tencent/ncnn)
84 |
--------------------------------------------------------------------------------
/tools/export_onnx.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | from loguru import logger
6 |
7 | import torch
8 | from torch import nn
9 |
10 | from yolox.exp import get_exp
11 | from yolox.models.network_blocks import SiLU
12 | from yolox.utils import replace_module
13 |
14 | import argparse
15 | import os
16 |
17 |
18 | def make_parser():
19 | parser = argparse.ArgumentParser("YOLOX onnx deploy")
20 | parser.add_argument(
21 | "--output-name", type=str, default="yolox.onnx", help="output name of models"
22 | )
23 | parser.add_argument(
24 | "--input", default="images", type=str, help="input node name of onnx model"
25 | )
26 | parser.add_argument(
27 | "--output", default="output", type=str, help="output node name of onnx model"
28 | )
29 | parser.add_argument(
30 | "-o", "--opset", default=11, type=int, help="onnx opset version"
31 | )
32 | parser.add_argument("--no-onnxsim", action="store_true", help="use onnxsim or not")
33 | parser.add_argument(
34 | "-f",
35 | "--exp_file",
36 | default=None,
37 | type=str,
38 | help="expriment description file",
39 | )
40 | parser.add_argument("-expn", "--experiment-name", type=str, default=None)
41 | parser.add_argument("-n", "--name", type=str, default=None, help="model name")
42 | parser.add_argument("-c", "--ckpt", default=None, type=str, help="ckpt path")
43 | parser.add_argument(
44 | "opts",
45 | help="Modify config options using the command-line",
46 | default=None,
47 | nargs=argparse.REMAINDER,
48 | )
49 |
50 | return parser
51 |
52 |
53 | @logger.catch
54 | def main():
55 | args = make_parser().parse_args()
56 | logger.info("args value: {}".format(args))
57 | exp = get_exp(args.exp_file, args.name)
58 | exp.merge(args.opts)
59 |
60 | if not args.experiment_name:
61 | args.experiment_name = exp.exp_name
62 |
63 | model = exp.get_model()
64 | if args.ckpt is None:
65 | file_name = os.path.join(exp.output_dir, args.experiment_name)
66 | ckpt_file = os.path.join(file_name, "best_ckpt.pth.tar")
67 | else:
68 | ckpt_file = args.ckpt
69 |
70 | # load the model state dict
71 | ckpt = torch.load(ckpt_file, map_location="cpu")
72 |
73 | model.eval()
74 | if "model" in ckpt:
75 | ckpt = ckpt["model"]
76 | model.load_state_dict(ckpt)
77 | model = replace_module(model, nn.SiLU, SiLU)
78 | model.head.decode_in_inference = False
79 |
80 | logger.info("loading checkpoint done.")
81 | dummy_input = torch.randn(1, 3, exp.test_size[0], exp.test_size[1])
82 | torch.onnx._export(
83 | model,
84 | dummy_input,
85 | args.output_name,
86 | input_names=[args.input],
87 | output_names=[args.output],
88 | opset_version=args.opset,
89 | )
90 | logger.info("generated onnx model named {}".format(args.output_name))
91 |
92 | if not args.no_onnxsim:
93 | import onnx
94 |
95 | from onnxsim import simplify
96 |
97 | # use onnxsimplify to reduce reduent model.
98 | onnx_model = onnx.load(args.output_name)
99 | model_simp, check = simplify(onnx_model)
100 | assert check, "Simplified ONNX model could not be validated"
101 | onnx.save(model_simp, args.output_name)
102 | logger.info("generated simplified onnx model named {}".format(args.output_name))
103 |
104 |
105 | if __name__ == "__main__":
106 | main()
107 |
--------------------------------------------------------------------------------
/yolox/data/samplers.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | import torch
6 | import torch.distributed as dist
7 | from torch.utils.data.sampler import BatchSampler as torchBatchSampler
8 | from torch.utils.data.sampler import Sampler
9 |
10 | import itertools
11 | from typing import Optional
12 |
13 |
14 | class YoloBatchSampler(torchBatchSampler):
15 | """
16 | This batch sampler will generate mini-batches of (dim, index) tuples from another sampler.
17 | It works just like the :class:`torch.utils.data.sampler.BatchSampler`,
18 | but it will prepend a dimension, whilst ensuring it stays the same across one mini-batch.
19 | """
20 |
21 | def __init__(self, *args, input_dimension=None, mosaic=True, **kwargs):
22 | super().__init__(*args, **kwargs)
23 | self.input_dim = input_dimension
24 | self.new_input_dim = None
25 | self.mosaic = mosaic
26 |
27 | def __iter__(self):
28 | self.__set_input_dim()
29 | for batch in super().__iter__():
30 | yield [(self.input_dim, idx, self.mosaic) for idx in batch]
31 | self.__set_input_dim()
32 |
33 | def __set_input_dim(self):
34 | """ This function randomly changes the the input dimension of the dataset. """
35 | if self.new_input_dim is not None:
36 | self.input_dim = (self.new_input_dim[0], self.new_input_dim[1])
37 | self.new_input_dim = None
38 |
39 |
40 | class InfiniteSampler(Sampler):
41 | """
42 | In training, we only care about the "infinite stream" of training data.
43 | So this sampler produces an infinite stream of indices and
44 | all workers cooperate to correctly shuffle the indices and sample different indices.
45 | The samplers in each worker effectively produces `indices[worker_id::num_workers]`
46 | where `indices` is an infinite stream of indices consisting of
47 | `shuffle(range(size)) + shuffle(range(size)) + ...` (if shuffle is True)
48 | or `range(size) + range(size) + ...` (if shuffle is False)
49 | """
50 |
51 | def __init__(
52 | self,
53 | size: int,
54 | shuffle: bool = True,
55 | seed: Optional[int] = 0,
56 | rank=0,
57 | world_size=1,
58 | ):
59 | """
60 | Args:
61 | size (int): the total number of data of the underlying dataset to sample from
62 | shuffle (bool): whether to shuffle the indices or not
63 | seed (int): the initial seed of the shuffle. Must be the same
64 | across all workers. If None, will use a random seed shared
65 | among workers (require synchronization among all workers).
66 | """
67 | self._size = size
68 | assert size > 0
69 | self._shuffle = shuffle
70 | self._seed = int(seed)
71 |
72 | if dist.is_available() and dist.is_initialized():
73 | self._rank = dist.get_rank()
74 | self._world_size = dist.get_world_size()
75 | else:
76 | self._rank = rank
77 | self._world_size = world_size
78 |
79 | def __iter__(self):
80 | start = self._rank
81 | yield from itertools.islice(
82 | self._infinite_indices(), start, None, self._world_size
83 | )
84 |
85 | def _infinite_indices(self):
86 | g = torch.Generator()
87 | g.manual_seed(self._seed)
88 | while True:
89 | if self._shuffle:
90 | yield from torch.randperm(self._size, generator=g)
91 | else:
92 | yield from torch.arange(self._size)
93 |
94 | def __len__(self):
95 | return self._size // self._world_size
96 |
--------------------------------------------------------------------------------
/yolox/utils/metric.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 | import numpy as np
5 |
6 | import torch
7 |
8 | import functools
9 | import os
10 | import time
11 | from collections import defaultdict, deque
12 |
13 | __all__ = [
14 | "AverageMeter",
15 | "MeterBuffer",
16 | "get_total_and_free_memory_in_Mb",
17 | "occupy_mem",
18 | "gpu_mem_usage",
19 | ]
20 |
21 |
22 | def get_total_and_free_memory_in_Mb(cuda_device):
23 | devices_info_str = os.popen(
24 | "nvidia-smi --query-gpu=memory.total,memory.used --format=csv,nounits,noheader"
25 | )
26 | devices_info = devices_info_str.read().strip().split("\n")
27 | total, used = devices_info[int(cuda_device)].split(",")
28 | return int(total), int(used)
29 |
30 |
31 | def occupy_mem(cuda_device, mem_ratio=0.9):
32 | """
33 | pre-allocate gpu memory for training to avoid memory Fragmentation.
34 | """
35 | total, used = get_total_and_free_memory_in_Mb(cuda_device)
36 | max_mem = int(total * mem_ratio)
37 | block_mem = max_mem - used
38 | x = torch.cuda.FloatTensor(256, 1024, block_mem)
39 | del x
40 | time.sleep(5)
41 |
42 |
43 | def gpu_mem_usage():
44 | """
45 | Compute the GPU memory usage for the current device (MB).
46 | """
47 | mem_usage_bytes = torch.cuda.max_memory_allocated()
48 | return mem_usage_bytes / (1024 * 1024)
49 |
50 |
51 | class AverageMeter:
52 | """Track a series of values and provide access to smoothed values over a
53 | window or the global series average.
54 | """
55 |
56 | def __init__(self, window_size=50):
57 | self._deque = deque(maxlen=window_size)
58 | self._total = 0.0
59 | self._count = 0
60 |
61 | def update(self, value):
62 | self._deque.append(value)
63 | self._count += 1
64 | self._total += value
65 |
66 | @property
67 | def median(self):
68 | d = np.array(list(self._deque))
69 | return np.median(d)
70 |
71 | @property
72 | def avg(self):
73 | # if deque is empty, nan will be returned.
74 | d = np.array(list(self._deque))
75 | return d.mean()
76 |
77 | @property
78 | def global_avg(self):
79 | return self._total / max(self._count, 1e-5)
80 |
81 | @property
82 | def latest(self):
83 | return self._deque[-1] if len(self._deque) > 0 else None
84 |
85 | @property
86 | def total(self):
87 | return self._total
88 |
89 | def reset(self):
90 | self._deque.clear()
91 | self._total = 0.0
92 | self._count = 0
93 |
94 | def clear(self):
95 | self._deque.clear()
96 |
97 |
98 | class MeterBuffer(defaultdict):
99 | """Computes and stores the average and current value"""
100 |
101 | def __init__(self, window_size=20):
102 | factory = functools.partial(AverageMeter, window_size=window_size)
103 | super().__init__(factory)
104 |
105 | def reset(self):
106 | for v in self.values():
107 | v.reset()
108 |
109 | def get_filtered_meter(self, filter_key="time"):
110 | return {k: v for k, v in self.items() if filter_key in k}
111 |
112 | def update(self, values=None, **kwargs):
113 | if values is None:
114 | values = {}
115 | values.update(kwargs)
116 | for k, v in values.items():
117 | if isinstance(v, torch.Tensor):
118 | v = v.detach()
119 | self[k].update(v)
120 |
121 | def clear_meters(self):
122 | for v in self.values():
123 | v.clear()
124 |
--------------------------------------------------------------------------------
/yolox/utils/model_utils.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import torch
6 | import torch.nn as nn
7 | from thop import profile
8 |
9 | from copy import deepcopy
10 |
11 | __all__ = [
12 | "fuse_conv_and_bn",
13 | "fuse_model",
14 | "get_model_info",
15 | "replace_module",
16 | ]
17 |
18 |
19 | def get_model_info(model, tsize):
20 |
21 | stride = 64
22 | img = torch.zeros((1, 3, stride, stride), device=next(model.parameters()).device)
23 | flops, params = profile(deepcopy(model), inputs=(img,), verbose=False)
24 | params /= 1e6
25 | flops /= 1e9
26 | flops *= tsize[0] * tsize[1] / stride / stride * 2 # Gflops
27 | info = "Params: {:.2f}M, Gflops: {:.2f}".format(params, flops)
28 | return info
29 |
30 |
31 | def fuse_conv_and_bn(conv, bn):
32 | # Fuse convolution and batchnorm layers https://tehnokv.com/posts/fusing-batchnorm-and-conv/
33 | fusedconv = (
34 | nn.Conv2d(
35 | conv.in_channels,
36 | conv.out_channels,
37 | kernel_size=conv.kernel_size,
38 | stride=conv.stride,
39 | padding=conv.padding,
40 | groups=conv.groups,
41 | bias=True,
42 | )
43 | .requires_grad_(False)
44 | .to(conv.weight.device)
45 | )
46 |
47 | # prepare filters
48 | w_conv = conv.weight.clone().view(conv.out_channels, -1)
49 | w_bn = torch.diag(bn.weight.div(torch.sqrt(bn.eps + bn.running_var)))
50 | fusedconv.weight.copy_(torch.mm(w_bn, w_conv).view(fusedconv.weight.shape))
51 |
52 | # prepare spatial bias
53 | b_conv = (
54 | torch.zeros(conv.weight.size(0), device=conv.weight.device)
55 | if conv.bias is None
56 | else conv.bias
57 | )
58 | b_bn = bn.bias - bn.weight.mul(bn.running_mean).div(
59 | torch.sqrt(bn.running_var + bn.eps)
60 | )
61 | fusedconv.bias.copy_(torch.mm(w_bn, b_conv.reshape(-1, 1)).reshape(-1) + b_bn)
62 |
63 | return fusedconv
64 |
65 |
66 | def fuse_model(model):
67 | from yolox.models.network_blocks import BaseConv
68 |
69 | for m in model.modules():
70 | if type(m) is BaseConv and hasattr(m, "bn"):
71 | m.conv = fuse_conv_and_bn(m.conv, m.bn) # update conv
72 | delattr(m, "bn") # remove batchnorm
73 | m.forward = m.fuseforward # update forward
74 | return model
75 |
76 |
77 | def replace_module(module, replaced_module_type, new_module_type, replace_func=None):
78 | """
79 | Replace given type in module to a new type. mostly used in deploy.
80 |
81 | Args:
82 | module (nn.Module): model to apply replace operation.
83 | replaced_module_type (Type): module type to be replaced.
84 | new_module_type (Type)
85 | replace_func (function): python function to describe replace logic. Defalut value None.
86 |
87 | Returns:
88 | model (nn.Module): module that already been replaced.
89 | """
90 |
91 | def default_replace_func(replaced_module_type, new_module_type):
92 | return new_module_type()
93 |
94 | if replace_func is None:
95 | replace_func = default_replace_func
96 |
97 | model = module
98 | if isinstance(module, replaced_module_type):
99 | model = replace_func(replaced_module_type, new_module_type)
100 | else: # recurrsively replace
101 | for name, child in module.named_children():
102 | new_child = replace_module(child, replaced_module_type, new_module_type)
103 | if new_child is not child: # child is already replaced
104 | model.add_module(name, new_child)
105 |
106 | return model
107 |
--------------------------------------------------------------------------------
/yolox/layers/csrc/cocoeval/cocoeval.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
2 | #pragma once
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | namespace py = pybind11;
11 |
12 | namespace COCOeval {
13 |
14 | // Annotation data for a single object instance in an image
15 | struct InstanceAnnotation {
16 | InstanceAnnotation(
17 | uint64_t id,
18 | double score,
19 | double area,
20 | bool is_crowd,
21 | bool ignore)
22 | : id{id}, score{score}, area{area}, is_crowd{is_crowd}, ignore{ignore} {}
23 | uint64_t id;
24 | double score = 0.;
25 | double area = 0.;
26 | bool is_crowd = false;
27 | bool ignore = false;
28 | };
29 |
30 | // Stores intermediate results for evaluating detection results for a single
31 | // image that has D detected instances and G ground truth instances. This stores
32 | // matches between detected and ground truth instances
33 | struct ImageEvaluation {
34 | // For each of the D detected instances, the id of the matched ground truth
35 | // instance, or 0 if unmatched
36 | std::vector detection_matches;
37 |
38 | // The detection score of each of the D detected instances
39 | std::vector detection_scores;
40 |
41 | // Marks whether or not each of G instances was ignored from evaluation (e.g.,
42 | // because it's outside area_range)
43 | std::vector ground_truth_ignores;
44 |
45 | // Marks whether or not each of D instances was ignored from evaluation (e.g.,
46 | // because it's outside aRng)
47 | std::vector detection_ignores;
48 | };
49 |
50 | template
51 | using ImageCategoryInstances = std::vector>>;
52 |
53 | // C++ implementation of COCO API cocoeval.py::COCOeval.evaluateImg(). For each
54 | // combination of image, category, area range settings, and IOU thresholds to
55 | // evaluate, it matches detected instances to ground truth instances and stores
56 | // the results into a vector of ImageEvaluation results, which will be
57 | // interpreted by the COCOeval::Accumulate() function to produce precion-recall
58 | // curves. The parameters of nested vectors have the following semantics:
59 | // image_category_ious[i][c][d][g] is the intersection over union of the d'th
60 | // detected instance and g'th ground truth instance of
61 | // category category_ids[c] in image image_ids[i]
62 | // image_category_ground_truth_instances[i][c] is a vector of ground truth
63 | // instances in image image_ids[i] of category category_ids[c]
64 | // image_category_detection_instances[i][c] is a vector of detected
65 | // instances in image image_ids[i] of category category_ids[c]
66 | std::vector EvaluateImages(
67 | const std::vector>& area_ranges, // vector of 2-tuples
68 | int max_detections,
69 | const std::vector& iou_thresholds,
70 | const ImageCategoryInstances>& image_category_ious,
71 | const ImageCategoryInstances&
72 | image_category_ground_truth_instances,
73 | const ImageCategoryInstances&
74 | image_category_detection_instances);
75 |
76 | // C++ implementation of COCOeval.accumulate(), which generates precision
77 | // recall curves for each set of category, IOU threshold, detection area range,
78 | // and max number of detections parameters. It is assumed that the parameter
79 | // evaluations is the return value of the functon COCOeval::EvaluateImages(),
80 | // which was called with the same parameter settings params
81 | py::dict Accumulate(
82 | const py::object& params,
83 | const std::vector& evalutations);
84 |
85 | } // namespace COCOeval
86 |
--------------------------------------------------------------------------------
/yolox/models/yolox.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- encoding: utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 | import numpy as np
5 | import cv2
6 | import torch.nn as nn
7 |
8 | from .yolo_head import YOLOXHead
9 | from .yolo_pafpn import YOLOPAFPN
10 |
11 |
12 | class YOLOX(nn.Module):
13 | """
14 | YOLOX model module. The module list is defined by create_yolov3_modules function.
15 | The network returns loss values from three YOLO layers during training
16 | and detection results during test.
17 | """
18 |
19 | def __init__(self, backbone=None, head=None):
20 | super().__init__()
21 | if backbone is None:
22 | backbone = YOLOPAFPN()
23 | if head is None:
24 | head = YOLOXHead(80)
25 |
26 | self.backbone = backbone
27 | self.head = head
28 |
29 | def forward(self, x, targets=None):
30 | #debug
31 | #self.show_train(x, targets)
32 |
33 | # fpn output content features of [dark3, dark4, dark5]
34 | fpn_outs = self.backbone(x)
35 |
36 | if self.training:
37 | assert targets is not None
38 | loss, iou_loss, conf_loss, cls_loss, l1_loss, lmk_loss, num_fg = self.head(
39 | fpn_outs, targets, x
40 | )
41 | outputs = {
42 | "total_loss": loss,
43 | "iou_loss": iou_loss,
44 | "l1_loss": l1_loss,
45 | "conf_loss": conf_loss,
46 | "cls_loss": cls_loss,
47 | "lmk_loss": lmk_loss,
48 | "num_fg": num_fg,
49 | }
50 | else:
51 | outputs = self.head(fpn_outs)
52 |
53 | return outputs
54 |
55 | def show_train(self, x, targets):
56 | print("show_train ....... ")
57 | rgb_means = np.array([0.485, 0.456, 0.406])
58 | std = np.array([0.229, 0.224, 0.225])
59 |
60 | x = x.cpu().numpy()
61 | if targets is not None:
62 | targets = targets.cpu().numpy()
63 | x[..., 0] = x[..., 0] * std[0] + rgb_means[0]
64 | x[..., 1] = x[..., 1] * std[1] + rgb_means[1]
65 | x[..., 2] = x[..., 2] * std[2] + rgb_means[2]
66 | x = np.clip(x, 0, 1.0)
67 | x = x * 255.0
68 | x = np.array(x, dtype=np.uint8)
69 |
70 | for i, target in enumerate(targets[0:x.shape[0]]):
71 | img = x[i]
72 | img = np.transpose(img, (1,2,0))
73 | img = img[:,:,::-1]
74 | img_cv = cv2.resize(img, (img.shape[1], img.shape[0]))
75 | boxes = target[...,1:5]
76 | cls = target[...,0]
77 | lmks = target[...,-10:]
78 | for j, box in enumerate(boxes):
79 | xj, yj, w, h = np.array(boxes[j], dtype=np.int32)
80 | x0 = int(xj - 0.5 * w)
81 | y0 = int(yj - 0.5 * h)
82 | x1 = int(xj + 0.5 * w)
83 | y1 = int(yj + 0.5 * h)
84 | if w == 0 or h == 0:
85 | continue
86 | #cv2.rectangle(img_cv,(x0, y0 + 1),(x0 + 1, y0),(0,0,255))
87 | img_cv = cv2.rectangle(img_cv, (x0, y0), (x1, y1), (0, 255, 0), 2)
88 | #cv2.putText(img, text, (x0, y0 + txt_size[1]), font, 0.4, txt_color, thickness=1)
89 | # landmarks
90 | clors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (0, 255, 255)]
91 | for k in range(5):
92 | point_x = int(lmks[j][2 * k])
93 | point_y = int(lmks[j][2 * k + 1])
94 | if point_x > 0 and point_y > 0:
95 | cv2.circle(img_cv, (point_x, point_y), 3, clors[k], -1)
96 | cv2.imwrite("./1_" + str(i)+'_'+str(j)+'.jpg', img_cv)
97 | exit(0)
98 |
99 |
--------------------------------------------------------------------------------
/demo/ONNXRuntime/README.md:
--------------------------------------------------------------------------------
1 | ## YOLOX-ONNXRuntime in Python
2 |
3 | This doc introduces how to convert your pytorch model into onnx, and how to run an onnxruntime demo to verify your convertion.
4 |
5 | ### Download ONNX models.
6 | | Model | Parameters | GFLOPs | Test Size | mAP | Weights |
7 | |:------| :----: | :----: | :---: | :---: | :---: |
8 | | YOLOX-Nano | 0.91M | 1.08 | 416x416 | 25.3 | [onedrive](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/EfAGwvevU-lNhW5OqFAyHbwBJdI_7EaKu5yU04fgF5BU7w?e=gvq4hf)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_nano.onnx) |
9 | | YOLOX-Tiny | 5.06M | 6.45 | 416x416 |31.7 | [onedrive](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/EVigCszU1ilDn-MwLwHCF1ABsgTy06xFdVgZ04Yyo4lHVA?e=hVKiCw)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_tiny.onnx) |
10 | | YOLOX-S | 9.0M | 26.8 | 640x640 |39.6 | [onedrive](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/Ec0L1d1x2UtIpbfiahgxhtgBZVjb1NCXbotO8SCOdMqpQQ?e=siyIsK)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_s.onnx) |
11 | | YOLOX-M | 25.3M | 73.8 | 640x640 |46.4 | [onedrive](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/ERUKlQe-nlxBoTKPy1ynbxsBmAZ_h-VBEV-nnfPdzUIkZQ?e=hyQQtl)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_m.onnx) |
12 | | YOLOX-L | 54.2M | 155.6 | 640x640 |50.0 | [onedrive](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/ET5w926jCA5GlVfg9ixB4KEBiW0HYl7SzaHNRaRG9dYO_A?e=ISmCYX)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_l.onnx) |
13 | | YOLOX-Darknet53| 63.72M | 185.3 | 640x640 |47.3 | [onedrive](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/ESArloSW-MlPlLuemLh9zKkBdovgweKbfu4zkvzKAp7pPQ?e=f81Ikw)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_darknet53.onnx) |
14 | | YOLOX-X | 99.1M | 281.9 | 640x640 |51.2 | [onedrive](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/ERjqoeMJlFdGuM3tQfXQmhABmGHlIHydWCwhlugeWLE9AA)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox.onnx) |
15 |
16 |
17 | ### Convert Your Model to ONNX
18 |
19 | First, you should move to by:
20 | ```shell
21 | cd
22 | ```
23 | Then, you can:
24 |
25 | 1. Convert a standard YOLOX model by -n:
26 | ```shell
27 | python3 tools/export_onnx.py --output-name yolox_s.onnx -n yolox-s -c yolox_s.pth.tar
28 | ```
29 | Notes:
30 | * -n: specify a model name. The model name must be one of the [yolox-s,m,l,x and yolox-nane, yolox-tiny, yolov3]
31 | * -c: the model you have trained
32 | * -o: opset version, default 11. **However, if you will further convert your onnx model to [OpenVINO](../OpenVINO/), please specify the opset version to 10.**
33 | * --no-onnxsim: disable onnxsim
34 | * To customize an input shape for onnx model, modify the following code in tools/export.py:
35 |
36 | ```python
37 | dummy_input = torch.randn(1, 3, exp.test_size[0], exp.test_size[1])
38 | ```
39 |
40 | 2. Convert a standard YOLOX model by -f. When using -f, the above command is equivalent to:
41 |
42 | ```shell
43 | python3 tools/export_onnx.py --output-name yolox_s.onnx -f exps/default/yolox_s.py -c yolox_s.pth.tar
44 | ```
45 |
46 | 3. To convert your customized model, please use -f:
47 |
48 | ```shell
49 | python3 tools/export_onnx.py --output-name your_yolox.onnx -f exps/your_dir/your_yolox.py -c your_yolox.pth.tar
50 | ```
51 |
52 | ### ONNXRuntime Demo
53 |
54 | Step1.
55 | ```shell
56 | cd /demo/ONNXRuntime
57 | ```
58 |
59 | Step2.
60 | ```shell
61 | python3 onnx_inference.py -m -i -o -s 0.3 --input_shape 640,640
62 | ```
63 | Notes:
64 | * -m: your converted onnx model
65 | * -i: input_image
66 | * -s: score threshold for visualization.
67 | * --input_shape: should be consistent with the shape you used for onnx convertion.
68 |
--------------------------------------------------------------------------------
/demo/MegEngine/python/models/yolo_pafpn.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- encoding: utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import megengine.module as M
6 | import megengine.functional as F
7 |
8 | from .darknet import CSPDarknet
9 | from .network_blocks import BaseConv, CSPLayer, DWConv, UpSample
10 |
11 |
12 | class YOLOPAFPN(M.Module):
13 | """
14 | YOLOv3 model. Darknet 53 is the default backbone of this model.
15 | """
16 |
17 | def __init__(
18 | self, depth=1.0, width=1.0, in_features=("dark3", "dark4", "dark5"),
19 | in_channels=[256, 512, 1024], depthwise=False, act="silu",
20 | ):
21 | super().__init__()
22 | self.backbone = CSPDarknet(depth, width, depthwise=depthwise, act=act)
23 | self.in_features = in_features
24 | self.in_channels = in_channels
25 | Conv = DWConv if depthwise else BaseConv
26 |
27 | self.upsample = UpSample(scale_factor=2, mode="bilinear")
28 | self.lateral_conv0 = BaseConv(
29 | int(in_channels[2] * width), int(in_channels[1] * width), 1, 1, act=act
30 | )
31 | self.C3_p4 = CSPLayer(
32 | int(2 * in_channels[1] * width),
33 | int(in_channels[1] * width),
34 | round(3 * depth),
35 | False,
36 | depthwise=depthwise,
37 | act=act,
38 | ) # cat
39 |
40 | self.reduce_conv1 = BaseConv(
41 | int(in_channels[1] * width), int(in_channels[0] * width), 1, 1, act=act
42 | )
43 | self.C3_p3 = CSPLayer(
44 | int(2 * in_channels[0] * width),
45 | int(in_channels[0] * width),
46 | round(3 * depth),
47 | False,
48 | depthwise=depthwise,
49 | act=act,
50 | )
51 |
52 | # bottom-up conv
53 | self.bu_conv2 = Conv(
54 | int(in_channels[0] * width), int(in_channels[0] * width), 3, 2, act=act
55 | )
56 | self.C3_n3 = CSPLayer(
57 | int(2 * in_channels[0] * width),
58 | int(in_channels[1] * width),
59 | round(3 * depth),
60 | False,
61 | depthwise=depthwise,
62 | act=act,
63 | )
64 |
65 | # bottom-up conv
66 | self.bu_conv1 = Conv(
67 | int(in_channels[1] * width), int(in_channels[1] * width), 3, 2, act=act
68 | )
69 | self.C3_n4 = CSPLayer(
70 | int(2 * in_channels[1] * width),
71 | int(in_channels[2] * width),
72 | round(3 * depth),
73 | False,
74 | depthwise=depthwise,
75 | act=act,
76 | )
77 |
78 | def forward(self, input):
79 | """
80 | Args:
81 | inputs: input images.
82 |
83 | Returns:
84 | Tuple[Tensor]: FPN feature.
85 | """
86 |
87 | # backbone
88 | out_features = self.backbone(input)
89 | features = [out_features[f] for f in self.in_features]
90 | [x2, x1, x0] = features
91 |
92 | fpn_out0 = self.lateral_conv0(x0) # 1024->512/32
93 | f_out0 = self.upsample(fpn_out0) # 512/16
94 | f_out0 = F.concat([f_out0, x1], 1) # 512->1024/16
95 | f_out0 = self.C3_p4(f_out0) # 1024->512/16
96 |
97 | fpn_out1 = self.reduce_conv1(f_out0) # 512->256/16
98 | f_out1 = self.upsample(fpn_out1) # 256/8
99 | f_out1 = F.concat([f_out1, x2], 1) # 256->512/8
100 | pan_out2 = self.C3_p3(f_out1) # 512->256/8
101 |
102 | p_out1 = self.bu_conv2(pan_out2) # 256->256/16
103 | p_out1 = F.concat([p_out1, fpn_out1], 1) # 256->512/16
104 | pan_out1 = self.C3_n3(p_out1) # 512->512/16
105 |
106 | p_out0 = self.bu_conv1(pan_out1) # 512->512/32
107 | p_out0 = F.concat([p_out0, fpn_out0], 1) # 512->1024/32
108 | pan_out0 = self.C3_n4(p_out0) # 1024->1024/32
109 |
110 | outputs = (pan_out2, pan_out1, pan_out0)
111 | return outputs
112 |
--------------------------------------------------------------------------------
/yolox/models/yolo_pafpn.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- encoding: utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import torch
6 | import torch.nn as nn
7 |
8 | from .darknet import CSPDarknet
9 | from .network_blocks import BaseConv, CSPLayer, DWConv
10 |
11 |
12 | class YOLOPAFPN(nn.Module):
13 | """
14 | YOLOv3 model. Darknet 53 is the default backbone of this model.
15 | """
16 |
17 | def __init__(
18 | self,
19 | depth=1.0,
20 | width=1.0,
21 | in_features=("dark3", "dark4", "dark5"),
22 | in_channels=[256, 512, 1024],
23 | depthwise=False,
24 | act="silu",
25 | ):
26 | super().__init__()
27 | self.backbone = CSPDarknet(depth, width, depthwise=depthwise, act=act)
28 | self.in_features = in_features
29 | self.in_channels = in_channels
30 | Conv = DWConv if depthwise else BaseConv
31 |
32 | self.upsample = nn.Upsample(scale_factor=2, mode="nearest")
33 | self.lateral_conv0 = BaseConv(
34 | int(in_channels[2] * width), int(in_channels[1] * width), 1, 1, act=act
35 | )
36 | self.C3_p4 = CSPLayer(
37 | int(2 * in_channels[1] * width),
38 | int(in_channels[1] * width),
39 | round(3 * depth),
40 | False,
41 | depthwise=depthwise,
42 | act=act,
43 | ) # cat
44 |
45 | self.reduce_conv1 = BaseConv(
46 | int(in_channels[1] * width), int(in_channels[0] * width), 1, 1, act=act
47 | )
48 | self.C3_p3 = CSPLayer(
49 | int(2 * in_channels[0] * width),
50 | int(in_channels[0] * width),
51 | round(3 * depth),
52 | False,
53 | depthwise=depthwise,
54 | act=act,
55 | )
56 |
57 | # bottom-up conv
58 | self.bu_conv2 = Conv(
59 | int(in_channels[0] * width), int(in_channels[0] * width), 3, 2, act=act
60 | )
61 | self.C3_n3 = CSPLayer(
62 | int(2 * in_channels[0] * width),
63 | int(in_channels[1] * width),
64 | round(3 * depth),
65 | False,
66 | depthwise=depthwise,
67 | act=act,
68 | )
69 |
70 | # bottom-up conv
71 | self.bu_conv1 = Conv(
72 | int(in_channels[1] * width), int(in_channels[1] * width), 3, 2, act=act
73 | )
74 | self.C3_n4 = CSPLayer(
75 | int(2 * in_channels[1] * width),
76 | int(in_channels[2] * width),
77 | round(3 * depth),
78 | False,
79 | depthwise=depthwise,
80 | act=act,
81 | )
82 |
83 | def forward(self, input):
84 | """
85 | Args:
86 | inputs: input images.
87 |
88 | Returns:
89 | Tuple[Tensor]: FPN feature.
90 | """
91 |
92 | # backbone
93 | out_features = self.backbone(input)
94 | features = [out_features[f] for f in self.in_features]
95 | [x2, x1, x0] = features
96 |
97 | fpn_out0 = self.lateral_conv0(x0) # 1024->512/32
98 | f_out0 = self.upsample(fpn_out0) # 512/16
99 | f_out0 = torch.cat([f_out0, x1], 1) # 512->1024/16
100 | f_out0 = self.C3_p4(f_out0) # 1024->512/16
101 |
102 | fpn_out1 = self.reduce_conv1(f_out0) # 512->256/16
103 | f_out1 = self.upsample(fpn_out1) # 256/8
104 | f_out1 = torch.cat([f_out1, x2], 1) # 256->512/8
105 | pan_out2 = self.C3_p3(f_out1) # 512->256/8
106 |
107 | p_out1 = self.bu_conv2(pan_out2) # 256->256/16
108 | p_out1 = torch.cat([p_out1, fpn_out1], 1) # 256->512/16
109 | pan_out1 = self.C3_n3(p_out1) # 512->512/16
110 |
111 | p_out0 = self.bu_conv1(pan_out1) # 512->512/32
112 | p_out0 = torch.cat([p_out0, fpn_out0], 1) # 512->1024/32
113 | pan_out0 = self.C3_n4(p_out0) # 1024->1024/32
114 |
115 | outputs = (pan_out2, pan_out1, pan_out0)
116 | return outputs
117 |
--------------------------------------------------------------------------------
/demo/MegEngine/python/visualize.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import cv2
6 | import numpy as np
7 |
8 | __all__ = ["vis"]
9 |
10 |
11 | def vis(img, boxes, scores, cls_ids, conf=0.5, class_names=None):
12 |
13 | for i in range(len(boxes)):
14 | box = boxes[i]
15 | cls_id = int(cls_ids[i])
16 | score = scores[i]
17 | if score < conf:
18 | continue
19 | x0 = int(box[0])
20 | y0 = int(box[1])
21 | x1 = int(box[2])
22 | y1 = int(box[3])
23 |
24 | color = (_COLORS[cls_id] * 255).astype(np.uint8).tolist()
25 | text = '{}:{:.1f}%'.format(class_names[cls_id], score * 100)
26 | txt_color = (0, 0, 0) if np.mean(_COLORS[cls_id]) > 0.5 else (255, 255, 255)
27 | font = cv2.FONT_HERSHEY_SIMPLEX
28 |
29 | txt_size = cv2.getTextSize(text, font, 0.4, 1)[0]
30 | cv2.rectangle(img, (x0, y0), (x1, y1), color, 2)
31 |
32 | txt_bk_color = (_COLORS[cls_id] * 255 * 0.7).astype(np.uint8).tolist()
33 | cv2.rectangle(
34 | img,
35 | (x0, y0 + 1),
36 | (x0 + txt_size[0] + 1, y0 + int(1.5*txt_size[1])),
37 | txt_bk_color,
38 | -1
39 | )
40 | cv2.putText(img, text, (x0, y0 + txt_size[1]), font, 0.4, txt_color, thickness=1)
41 |
42 | return img
43 |
44 |
45 | _COLORS = np.array(
46 | [
47 | 0.000, 0.447, 0.741,
48 | 0.850, 0.325, 0.098,
49 | 0.929, 0.694, 0.125,
50 | 0.494, 0.184, 0.556,
51 | 0.466, 0.674, 0.188,
52 | 0.301, 0.745, 0.933,
53 | 0.635, 0.078, 0.184,
54 | 0.300, 0.300, 0.300,
55 | 0.600, 0.600, 0.600,
56 | 1.000, 0.000, 0.000,
57 | 1.000, 0.500, 0.000,
58 | 0.749, 0.749, 0.000,
59 | 0.000, 1.000, 0.000,
60 | 0.000, 0.000, 1.000,
61 | 0.667, 0.000, 1.000,
62 | 0.333, 0.333, 0.000,
63 | 0.333, 0.667, 0.000,
64 | 0.333, 1.000, 0.000,
65 | 0.667, 0.333, 0.000,
66 | 0.667, 0.667, 0.000,
67 | 0.667, 1.000, 0.000,
68 | 1.000, 0.333, 0.000,
69 | 1.000, 0.667, 0.000,
70 | 1.000, 1.000, 0.000,
71 | 0.000, 0.333, 0.500,
72 | 0.000, 0.667, 0.500,
73 | 0.000, 1.000, 0.500,
74 | 0.333, 0.000, 0.500,
75 | 0.333, 0.333, 0.500,
76 | 0.333, 0.667, 0.500,
77 | 0.333, 1.000, 0.500,
78 | 0.667, 0.000, 0.500,
79 | 0.667, 0.333, 0.500,
80 | 0.667, 0.667, 0.500,
81 | 0.667, 1.000, 0.500,
82 | 1.000, 0.000, 0.500,
83 | 1.000, 0.333, 0.500,
84 | 1.000, 0.667, 0.500,
85 | 1.000, 1.000, 0.500,
86 | 0.000, 0.333, 1.000,
87 | 0.000, 0.667, 1.000,
88 | 0.000, 1.000, 1.000,
89 | 0.333, 0.000, 1.000,
90 | 0.333, 0.333, 1.000,
91 | 0.333, 0.667, 1.000,
92 | 0.333, 1.000, 1.000,
93 | 0.667, 0.000, 1.000,
94 | 0.667, 0.333, 1.000,
95 | 0.667, 0.667, 1.000,
96 | 0.667, 1.000, 1.000,
97 | 1.000, 0.000, 1.000,
98 | 1.000, 0.333, 1.000,
99 | 1.000, 0.667, 1.000,
100 | 0.333, 0.000, 0.000,
101 | 0.500, 0.000, 0.000,
102 | 0.667, 0.000, 0.000,
103 | 0.833, 0.000, 0.000,
104 | 1.000, 0.000, 0.000,
105 | 0.000, 0.167, 0.000,
106 | 0.000, 0.333, 0.000,
107 | 0.000, 0.500, 0.000,
108 | 0.000, 0.667, 0.000,
109 | 0.000, 0.833, 0.000,
110 | 0.000, 1.000, 0.000,
111 | 0.000, 0.000, 0.167,
112 | 0.000, 0.000, 0.333,
113 | 0.000, 0.000, 0.500,
114 | 0.000, 0.000, 0.667,
115 | 0.000, 0.000, 0.833,
116 | 0.000, 0.000, 1.000,
117 | 0.000, 0.000, 0.000,
118 | 0.143, 0.143, 0.143,
119 | 0.286, 0.286, 0.286,
120 | 0.429, 0.429, 0.429,
121 | 0.571, 0.571, 0.571,
122 | 0.714, 0.714, 0.714,
123 | 0.857, 0.857, 0.857,
124 | 0.000, 0.447, 0.741,
125 | 0.314, 0.717, 0.741,
126 | 0.50, 0.5, 0
127 | ]
128 | ).astype(np.float32).reshape(-1, 3)
129 |
--------------------------------------------------------------------------------
/tools/train.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | from loguru import logger
6 |
7 | import torch
8 | import torch.backends.cudnn as cudnn
9 |
10 | from yolox.core import Trainer, launch
11 | from yolox.exp import get_exp
12 | from yolox.utils import configure_nccl
13 |
14 | import argparse
15 | import random
16 | import warnings
17 |
18 |
19 | def make_parser():
20 | parser = argparse.ArgumentParser("YOLOX train parser")
21 | parser.add_argument("-expn", "--experiment-name", type=str, default=None)
22 | parser.add_argument("-n", "--name", type=str, default=None, help="model name")
23 |
24 | # distributed
25 | parser.add_argument(
26 | "--dist-backend", default="nccl", type=str, help="distributed backend"
27 | )
28 | parser.add_argument(
29 | "--dist-url",
30 | default=None,
31 | type=str,
32 | help="url used to set up distributed training",
33 | )
34 | parser.add_argument("-b", "--batch-size", type=int, default=16, help="batch size")
35 | parser.add_argument(
36 | "-d", "--devices", default=None, type=int, help="device for training"
37 | )
38 | parser.add_argument(
39 | "--local_rank", default=0, type=int, help="local rank for dist training"
40 | )
41 | parser.add_argument(
42 | "-f",
43 | "--exp_file",
44 | default="./exps/example/yolox_voc/yolox_yolo_s.py",
45 | type=str,
46 | help="plz input your expriment description file",
47 | )
48 | parser.add_argument(
49 | "--resume", default=True, action="store_true", help="resume training"
50 | )
51 | parser.add_argument("-c", "--ckpt", default=None, type=str, help="checkpoint file")
52 | parser.add_argument(
53 | "-e",
54 | "--start_epoch",
55 | default=None,
56 | type=int,
57 | help="resume training start epoch",
58 | )
59 | parser.add_argument(
60 | "--num_machine", default=1, type=int, help="num of node for training"
61 | )
62 | parser.add_argument(
63 | "--machine_rank", default=0, type=int, help="node rank for multi-node training"
64 | )
65 | parser.add_argument(
66 | "--fp16",
67 | dest="fp16",
68 | default=True,
69 | action="store_true",
70 | help="Adopting mix precision training.",
71 | )
72 | parser.add_argument(
73 | "-o",
74 | "--occupy",
75 | dest="occupy",
76 | default=False,
77 | action="store_true",
78 | help="occupy GPU memory first for training.",
79 | )
80 | parser.add_argument(
81 | "opts",
82 | help="Modify config options using the command-line",
83 | default=None,
84 | nargs=argparse.REMAINDER,
85 | )
86 | return parser
87 |
88 |
89 | @logger.catch
90 | def main(exp, args):
91 | if not args.experiment_name:
92 | args.experiment_name = exp.exp_name
93 |
94 | if exp.seed is not None:
95 | random.seed(exp.seed)
96 | torch.manual_seed(exp.seed)
97 | cudnn.deterministic = True
98 | warnings.warn(
99 | "You have chosen to seed training. This will turn on the CUDNN deterministic setting, "
100 | "which can slow down your training considerably! You may see unexpected behavior "
101 | "when restarting from checkpoints."
102 | )
103 |
104 | # set environment variables for distributed training
105 | configure_nccl()
106 | cudnn.benchmark = True
107 |
108 | trainer = Trainer(exp, args)
109 | trainer.train()
110 |
111 |
112 | if __name__ == "__main__":
113 | args = make_parser().parse_args()
114 | exp = get_exp(args.exp_file, args.name)
115 | exp.merge(args.opts)
116 |
117 | num_gpu = torch.cuda.device_count() if args.devices is None else args.devices
118 | assert num_gpu <= torch.cuda.device_count()
119 |
120 | launch(
121 | main,
122 | num_gpu,
123 | args.num_machine,
124 | args.machine_rank,
125 | backend=args.dist_backend,
126 | dist_url=args.dist_url,
127 | args=(exp, args),
128 | )
129 |
--------------------------------------------------------------------------------
/demo/OpenVINO/cpp/README.md:
--------------------------------------------------------------------------------
1 | # YOLOX-OpenVINO in C++
2 |
3 | This toturial includes a C++ demo for OpenVINO, as well as some converted models.
4 |
5 | ### Download OpenVINO models.
6 | | Model | Parameters | GFLOPs | Test Size | mAP | Weights |
7 | |:------| :----: | :----: | :---: | :---: | :---: |
8 | | [YOLOX-Nano](../../../exps/nano.py) | 0.91M | 1.08 | 416x416 | 25.3 | [Download](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/EeWY57o5wQZFtXYd1KJw6Z8B4vxZru649XxQHYIFgio3Qw?e=ZS81ce)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_nano_openvino.tar.gz) |
9 | | [YOLOX-Tiny](../../../exps/yolox_tiny.py) | 5.06M | 6.45 | 416x416 |31.7 | [Download](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/ETfvOoCXdVZNinoSpKA_sEYBIQVqfjjF5_M6VvHRnLVcsA?e=STL1pi)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_tiny_openvino.tar.gz) |
10 | | [YOLOX-S](../../../exps/yolox_s.py) | 9.0M | 26.8 | 640x640 |39.6 | [Download](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/EXUjf3PQnbBLrxNrXPueqaIBzVZOrYQOnJpLK1Fytj5ssA?e=GK0LOM)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_s_openvino.tar.gz) |
11 | | [YOLOX-M](../../../exps/yolox_m.py) | 25.3M | 73.8 | 640x640 |46.4 | [Download](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/EcoT1BPpeRpLvE_4c441zn8BVNCQ2naxDH3rho7WqdlgLQ?e=95VaM9)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_m_openvino.tar.gz) |
12 | | [YOLOX-L](../../../exps/yolox_l.py) | 54.2M | 155.6 | 640x640 |50.0 | [Download](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/EZvmn-YLRuVPh0GAP_w3xHMB2VGvrKqQXyK_Cv5yi_DXUg?e=YRh6Eq)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_l_openvino.tar.gz) |
13 | | [YOLOX-Darknet53](../../../exps/yolov3.py) | 63.72M | 185.3 | 640x640 |47.3 | [Download](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/EQP8LSroikFHuwX0jFRetmcBOCDWSFmylHxolV7ezUPXGw?e=bEw5iq)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_darknet53_openvino.tar.gz) |
14 | | [YOLOX-X](../../../exps/yolox_x.py) | 99.1M | 281.9 | 640x640 |51.2 | [Download](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/EZFPnLqiD-xIlt7rcZYDjQgB4YXE9wnq1qaSXQwJrsKbdg?e=83nwEz)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_x_openvino.tar.gz) |
15 |
16 | ## Install OpenVINO Toolkit
17 |
18 | Please visit [Openvino Homepage](https://docs.openvinotoolkit.org/latest/get_started_guides.html) for more details.
19 |
20 | ## Set up the Environment
21 |
22 | ### For Linux
23 |
24 | **Option1. Set up the environment tempororally. You need to run this command everytime you start a new shell window.**
25 |
26 | ```shell
27 | source /opt/intel/openvino_2021/bin/setupvars.sh
28 | ```
29 |
30 | **Option2. Set up the environment permenantly.**
31 |
32 | *Step1.* For Linux:
33 | ```shell
34 | vim ~/.bashrc
35 | ```
36 |
37 | *Step2.* Add the following line into your file:
38 |
39 | ```shell
40 | source /opt/intel/openvino_2021/bin/setupvars.sh
41 | ```
42 |
43 | *Step3.* Save and exit the file, then run:
44 |
45 | ```shell
46 | source ~/.bashrc
47 | ```
48 |
49 |
50 | ## Convert model
51 |
52 | 1. Export ONNX model
53 |
54 | Please refer to the [ONNX toturial](../../ONNXRuntime). **Note that you should set --opset to 10, otherwise your next step will fail.**
55 |
56 | 2. Convert ONNX to OpenVINO
57 |
58 | ``` shell
59 | cd /openvino_2021/deployment_tools/model_optimizer
60 | ```
61 |
62 | Install requirements for convert tool
63 |
64 | ```shell
65 | sudo ./install_prerequisites/install_prerequisites_onnx.sh
66 | ```
67 |
68 | Then convert model.
69 | ```shell
70 | python3 mo.py --input_model --input_shape [--data_type FP16]
71 | ```
72 | For example:
73 | ```shell
74 | python3 mo.py --input_model yolox.onnx --input_shape (1,3,640,640) --data_type FP16
75 | ```
76 |
77 | ## Build
78 |
79 | ### Linux
80 | ```shell
81 | source /opt/intel/openvino_2021/bin/setupvars.sh
82 | mkdir build
83 | cd build
84 | cmake ..
85 | make
86 | ```
87 |
88 | ## Demo
89 |
90 | ### c++
91 |
92 | ```shell
93 | ./yolox_openvino
94 | ```
95 |
--------------------------------------------------------------------------------
/demo/OpenVINO/python/README.md:
--------------------------------------------------------------------------------
1 | # YOLOX-OpenVINO in Python
2 |
3 | This toturial includes a Python demo for OpenVINO, as well as some converted models.
4 |
5 | ### Download OpenVINO models.
6 | | Model | Parameters | GFLOPs | Test Size | mAP | Weights |
7 | |:------| :----: | :----: | :---: | :---: | :---: |
8 | | [YOLOX-Nano](../../../exps/default/nano.py) | 0.91M | 1.08 | 416x416 | 25.3 | [onedrive](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/EeWY57o5wQZFtXYd1KJw6Z8B4vxZru649XxQHYIFgio3Qw?e=ZS81ce)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_nano_openvino.tar.gz) |
9 | | [YOLOX-Tiny](../../../exps/default/yolox_tiny.py) | 5.06M | 6.45 | 416x416 |31.7 | [onedrive](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/ETfvOoCXdVZNinoSpKA_sEYBIQVqfjjF5_M6VvHRnLVcsA?e=STL1pi)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_tiny_openvino.tar.gz) |
10 | | [YOLOX-S](../../../exps/default/yolox_s.py) | 9.0M | 26.8 | 640x640 |39.6 | [onedrive](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/EXUjf3PQnbBLrxNrXPueqaIBzVZOrYQOnJpLK1Fytj5ssA?e=GK0LOM)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_s_openvino.tar.gz) |
11 | | [YOLOX-M](../../../exps/default/yolox_m.py) | 25.3M | 73.8 | 640x640 |46.4 | [onedrive](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/EcoT1BPpeRpLvE_4c441zn8BVNCQ2naxDH3rho7WqdlgLQ?e=95VaM9)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_m_openvino.tar.gz) |
12 | | [YOLOX-L](../../../exps/default/yolox_l.py) | 54.2M | 155.6 | 640x640 |50.0 | [onedrive](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/EZvmn-YLRuVPh0GAP_w3xHMB2VGvrKqQXyK_Cv5yi_DXUg?e=YRh6Eq)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_l_openvino.tar.gz) |
13 | | [YOLOX-Darknet53](../../../exps/default/yolov3.py) | 63.72M | 185.3 | 640x640 |47.3 | [onedrive](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/EQP8LSroikFHuwX0jFRetmcBOCDWSFmylHxolV7ezUPXGw?e=bEw5iq)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_darknet53_openvino.tar.gz) |
14 | | [YOLOX-X](../../../exps/default/yolox_x.py) | 99.1M | 281.9 | 640x640 |51.2 | [onedrive](https://megvii-my.sharepoint.cn/:u:/g/personal/gezheng_megvii_com/EZFPnLqiD-xIlt7rcZYDjQgB4YXE9wnq1qaSXQwJrsKbdg?e=83nwEz)/[github](https://github.com/Megvii-BaseDetection/storage/releases/download/0.0.1/yolox_x_openvino.tar.gz) |
15 |
16 | ## Install OpenVINO Toolkit
17 |
18 | Please visit [Openvino Homepage](https://docs.openvinotoolkit.org/latest/get_started_guides.html) for more details.
19 |
20 | ## Set up the Environment
21 |
22 | ### For Linux
23 |
24 | **Option1. Set up the environment tempororally. You need to run this command everytime you start a new shell window.**
25 |
26 | ```shell
27 | source /opt/intel/openvino_2021/bin/setupvars.sh
28 | ```
29 |
30 | **Option2. Set up the environment permenantly.**
31 |
32 | *Step1.* For Linux:
33 | ```shell
34 | vim ~/.bashrc
35 | ```
36 |
37 | *Step2.* Add the following line into your file:
38 |
39 | ```shell
40 | source /opt/intel/openvino_2021/bin/setupvars.sh
41 | ```
42 |
43 | *Step3.* Save and exit the file, then run:
44 |
45 | ```shell
46 | source ~/.bashrc
47 | ```
48 |
49 |
50 | ## Convert model
51 |
52 | 1. Export ONNX model
53 |
54 | Please refer to the [ONNX toturial](../../ONNXRuntime). **Note that you should set --opset to 10, otherwise your next step will fail.**
55 |
56 | 2. Convert ONNX to OpenVINO
57 |
58 | ``` shell
59 | cd /openvino_2021/deployment_tools/model_optimizer
60 | ```
61 |
62 | Install requirements for convert tool
63 |
64 | ```shell
65 | sudo ./install_prerequisites/install_prerequisites_onnx.sh
66 | ```
67 |
68 | Then convert model.
69 | ```shell
70 | python3 mo.py --input_model --input_shape [--data_type FP16]
71 | ```
72 | For example:
73 | ```shell
74 | python3 mo.py --input_model yolox.onnx --input_shape [1,3,640,640] --data_type FP16 --output_dir converted_output
75 | ```
76 |
77 | ## Demo
78 |
79 | ### python
80 |
81 | ```shell
82 | python openvino_inference.py -m -i
83 | ```
84 | or
85 | ```shell
86 | python openvino_inference.py -m -i -o -s -d
87 | ```
88 |
89 |
--------------------------------------------------------------------------------
/exps/example/yolox_voc/yolox_voc_s.py:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 | import os
3 | import random
4 | import torch
5 | import torch.nn as nn
6 | import torch.distributed as dist
7 |
8 | from yolox.exp import Exp as MyExp
9 | from yolox.data import get_yolox_datadir
10 |
11 | class Exp(MyExp):
12 | def __init__(self):
13 | super(Exp, self).__init__()
14 | self.num_classes = 20
15 | self.depth = 0.33
16 | self.width = 0.50
17 | self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]
18 |
19 | def get_data_loader(self, batch_size, is_distributed, no_aug=False):
20 | from yolox.data import (
21 | VOCDetection,
22 | TrainTransform,
23 | YoloBatchSampler,
24 | DataLoader,
25 | InfiniteSampler,
26 | MosaicDetection,
27 | )
28 |
29 | dataset = VOCDetection(
30 | data_dir=os.path.join(get_yolox_datadir(), "VOCdevkit"),
31 | image_sets=[('2007', 'trainval'), ('2012', 'trainval')],
32 | img_size=self.input_size,
33 | preproc=TrainTransform(
34 | rgb_means=(0.485, 0.456, 0.406),
35 | std=(0.229, 0.224, 0.225),
36 | max_labels=50,
37 | ),
38 | )
39 |
40 | dataset = MosaicDetection(
41 | dataset,
42 | mosaic=not no_aug,
43 | img_size=self.input_size,
44 | preproc=TrainTransform(
45 | rgb_means=(0.485, 0.456, 0.406),
46 | std=(0.229, 0.224, 0.225),
47 | max_labels=120,
48 | ),
49 | degrees=self.degrees,
50 | translate=self.translate,
51 | scale=self.scale,
52 | shear=self.shear,
53 | perspective=self.perspective,
54 | enable_mixup=self.enable_mixup,
55 | )
56 |
57 | self.dataset = dataset
58 |
59 | if is_distributed:
60 | batch_size = batch_size // dist.get_world_size()
61 |
62 | sampler = InfiniteSampler(
63 | len(self.dataset), seed=self.seed if self.seed else 0
64 | )
65 |
66 | batch_sampler = YoloBatchSampler(
67 | sampler=sampler,
68 | batch_size=batch_size,
69 | drop_last=False,
70 | input_dimension=self.input_size,
71 | mosaic=not no_aug,
72 | )
73 |
74 | dataloader_kwargs = {"num_workers": self.data_num_workers, "pin_memory": True}
75 | dataloader_kwargs["batch_sampler"] = batch_sampler
76 | train_loader = DataLoader(self.dataset, **dataloader_kwargs)
77 |
78 | return train_loader
79 |
80 | def get_eval_loader(self, batch_size, is_distributed, testdev=False):
81 | from yolox.data import VOCDetection, ValTransform
82 |
83 | valdataset = VOCDetection(
84 | data_dir=os.path.join(get_yolox_datadir(), "VOCdevkit"),
85 | image_sets=[('2007', 'test')],
86 | img_size=self.test_size,
87 | preproc=ValTransform(
88 | rgb_means=(0.485, 0.456, 0.406),
89 | std=(0.229, 0.224, 0.225),
90 | ),
91 | )
92 |
93 | if is_distributed:
94 | batch_size = batch_size // dist.get_world_size()
95 | sampler = torch.utils.data.distributed.DistributedSampler(
96 | valdataset, shuffle=False
97 | )
98 | else:
99 | sampler = torch.utils.data.SequentialSampler(valdataset)
100 |
101 | dataloader_kwargs = {
102 | "num_workers": self.data_num_workers,
103 | "pin_memory": True,
104 | "sampler": sampler,
105 | }
106 | dataloader_kwargs["batch_size"] = batch_size
107 | val_loader = torch.utils.data.DataLoader(valdataset, **dataloader_kwargs)
108 |
109 | return val_loader
110 |
111 | def get_evaluator(self, batch_size, is_distributed, testdev=False):
112 | from yolox.evaluators import VOCEvaluator
113 |
114 | val_loader = self.get_eval_loader(batch_size, is_distributed, testdev=testdev)
115 | evaluator = VOCEvaluator(
116 | dataloader=val_loader,
117 | img_size=self.test_size,
118 | confthre=self.test_conf,
119 | nmsthre=self.nmsthre,
120 | num_classes=self.num_classes,
121 | )
122 | return evaluator
123 |
--------------------------------------------------------------------------------
/yolox/utils/visualize.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import cv2
6 | import numpy as np
7 |
8 | __all__ = ["vis"]
9 |
10 |
11 | def vis(img, boxes, scores, landmarks, cls_ids, conf=0.5, class_names=None):
12 |
13 | for i in range(len(boxes)):
14 | box = boxes[i]
15 | cls_id = int(cls_ids[i])
16 | score = scores[i]
17 | landmark = landmarks[i]
18 | if score < conf:
19 | continue
20 | x0 = int(box[0])
21 | y0 = int(box[1])
22 | x1 = int(box[2])
23 | y1 = int(box[3])
24 |
25 | color = (_COLORS[cls_id] * 255).astype(np.uint8).tolist()
26 | text = '{}:{:.1f}%'.format(class_names[cls_id], score * 100)
27 | txt_color = (0, 0, 0) if np.mean(_COLORS[cls_id]) > 0.5 else (255, 255, 255)
28 | font = cv2.FONT_HERSHEY_SIMPLEX
29 |
30 | txt_size = cv2.getTextSize(text, font, 0.4, 1)[0]
31 | cv2.rectangle(img, (x0, y0), (x1, y1), color, 2)
32 |
33 | txt_bk_color = (_COLORS[cls_id] * 255 * 0.7).astype(np.uint8).tolist()
34 | cv2.rectangle(
35 | img,
36 | (x0, y0 + 1),
37 | (x0 + txt_size[0] + 1, y0 + int(1.5*txt_size[1])),
38 | txt_bk_color,
39 | -1
40 | )
41 | cv2.putText(img, text, (x0, y0 + txt_size[1]), font, 0.4, txt_color, thickness=1)
42 |
43 | #landmarks
44 | clors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (0, 255, 255)]
45 | for k in range(5):
46 | point_x = int(landmark[2 * k])
47 | point_y = int(landmark[2 * k + 1])
48 | cv2.circle(img, (point_x, point_y), 3 , clors[k], -1)
49 |
50 | return img
51 |
52 |
53 | _COLORS = np.array(
54 | [
55 | 0.000, 0.447, 0.741,
56 | 0.850, 0.325, 0.098,
57 | 0.929, 0.694, 0.125,
58 | 0.494, 0.184, 0.556,
59 | 0.466, 0.674, 0.188,
60 | 0.301, 0.745, 0.933,
61 | 0.635, 0.078, 0.184,
62 | 0.300, 0.300, 0.300,
63 | 0.600, 0.600, 0.600,
64 | 1.000, 0.000, 0.000,
65 | 1.000, 0.500, 0.000,
66 | 0.749, 0.749, 0.000,
67 | 0.000, 1.000, 0.000,
68 | 0.000, 0.000, 1.000,
69 | 0.667, 0.000, 1.000,
70 | 0.333, 0.333, 0.000,
71 | 0.333, 0.667, 0.000,
72 | 0.333, 1.000, 0.000,
73 | 0.667, 0.333, 0.000,
74 | 0.667, 0.667, 0.000,
75 | 0.667, 1.000, 0.000,
76 | 1.000, 0.333, 0.000,
77 | 1.000, 0.667, 0.000,
78 | 1.000, 1.000, 0.000,
79 | 0.000, 0.333, 0.500,
80 | 0.000, 0.667, 0.500,
81 | 0.000, 1.000, 0.500,
82 | 0.333, 0.000, 0.500,
83 | 0.333, 0.333, 0.500,
84 | 0.333, 0.667, 0.500,
85 | 0.333, 1.000, 0.500,
86 | 0.667, 0.000, 0.500,
87 | 0.667, 0.333, 0.500,
88 | 0.667, 0.667, 0.500,
89 | 0.667, 1.000, 0.500,
90 | 1.000, 0.000, 0.500,
91 | 1.000, 0.333, 0.500,
92 | 1.000, 0.667, 0.500,
93 | 1.000, 1.000, 0.500,
94 | 0.000, 0.333, 1.000,
95 | 0.000, 0.667, 1.000,
96 | 0.000, 1.000, 1.000,
97 | 0.333, 0.000, 1.000,
98 | 0.333, 0.333, 1.000,
99 | 0.333, 0.667, 1.000,
100 | 0.333, 1.000, 1.000,
101 | 0.667, 0.000, 1.000,
102 | 0.667, 0.333, 1.000,
103 | 0.667, 0.667, 1.000,
104 | 0.667, 1.000, 1.000,
105 | 1.000, 0.000, 1.000,
106 | 1.000, 0.333, 1.000,
107 | 1.000, 0.667, 1.000,
108 | 0.333, 0.000, 0.000,
109 | 0.500, 0.000, 0.000,
110 | 0.667, 0.000, 0.000,
111 | 0.833, 0.000, 0.000,
112 | 1.000, 0.000, 0.000,
113 | 0.000, 0.167, 0.000,
114 | 0.000, 0.333, 0.000,
115 | 0.000, 0.500, 0.000,
116 | 0.000, 0.667, 0.000,
117 | 0.000, 0.833, 0.000,
118 | 0.000, 1.000, 0.000,
119 | 0.000, 0.000, 0.167,
120 | 0.000, 0.000, 0.333,
121 | 0.000, 0.000, 0.500,
122 | 0.000, 0.000, 0.667,
123 | 0.000, 0.000, 0.833,
124 | 0.000, 0.000, 1.000,
125 | 0.000, 0.000, 0.000,
126 | 0.143, 0.143, 0.143,
127 | 0.286, 0.286, 0.286,
128 | 0.429, 0.429, 0.429,
129 | 0.571, 0.571, 0.571,
130 | 0.714, 0.714, 0.714,
131 | 0.857, 0.857, 0.857,
132 | 0.000, 0.447, 0.741,
133 | 0.314, 0.717, 0.741,
134 | 0.50, 0.5, 0
135 | ]
136 | ).astype(np.float32).reshape(-1, 3)
137 |
--------------------------------------------------------------------------------
/yolox/data/datasets/datasets_wrapper.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | from torch.utils.data.dataset import ConcatDataset as torchConcatDataset
6 | from torch.utils.data.dataset import Dataset as torchDataset
7 |
8 | import bisect
9 | from functools import wraps
10 |
11 |
12 | class ConcatDataset(torchConcatDataset):
13 | def __init__(self, datasets):
14 | super(ConcatDataset, self).__init__(datasets)
15 | if hasattr(self.datasets[0], "input_dim"):
16 | self._input_dim = self.datasets[0].input_dim
17 | self.input_dim = self.datasets[0].input_dim
18 |
19 | def pull_item(self, idx):
20 | if idx < 0:
21 | if -idx > len(self):
22 | raise ValueError(
23 | "absolute value of index should not exceed dataset length"
24 | )
25 | idx = len(self) + idx
26 | dataset_idx = bisect.bisect_right(self.cumulative_sizes, idx)
27 | if dataset_idx == 0:
28 | sample_idx = idx
29 | else:
30 | sample_idx = idx - self.cumulative_sizes[dataset_idx - 1]
31 | return self.datasets[dataset_idx].pull_item(sample_idx)
32 |
33 |
34 | class MixConcatDataset(torchConcatDataset):
35 | def __init__(self, datasets):
36 | super(MixConcatDataset, self).__init__(datasets)
37 | if hasattr(self.datasets[0], "input_dim"):
38 | self._input_dim = self.datasets[0].input_dim
39 | self.input_dim = self.datasets[0].input_dim
40 |
41 | def __getitem__(self, index):
42 |
43 | if not isinstance(index, int):
44 | idx = index[1]
45 | if idx < 0:
46 | if -idx > len(self):
47 | raise ValueError(
48 | "absolute value of index should not exceed dataset length"
49 | )
50 | idx = len(self) + idx
51 | dataset_idx = bisect.bisect_right(self.cumulative_sizes, idx)
52 | if dataset_idx == 0:
53 | sample_idx = idx
54 | else:
55 | sample_idx = idx - self.cumulative_sizes[dataset_idx - 1]
56 | if not isinstance(index, int):
57 | index = (index[0], sample_idx, index[2])
58 |
59 | return self.datasets[dataset_idx][index]
60 |
61 |
62 | class Dataset(torchDataset):
63 | """ This class is a subclass of the base :class:`torch.utils.data.Dataset`,
64 | that enables on the fly resizing of the ``input_dim``.
65 |
66 | Args:
67 | input_dimension (tuple): (width,height) tuple with default dimensions of the network
68 | """
69 |
70 | def __init__(self, input_dimension, mosaic=True):
71 | super().__init__()
72 | self.__input_dim = input_dimension[:2]
73 | self._mosaic = mosaic
74 |
75 | @property
76 | def input_dim(self):
77 | """
78 | Dimension that can be used by transforms to set the correct image size, etc.
79 | This allows transforms to have a single source of truth
80 | for the input dimension of the network.
81 |
82 | Return:
83 | list: Tuple containing the current width,height
84 | """
85 | if hasattr(self, "_input_dim"):
86 | return self._input_dim
87 | return self.__input_dim
88 |
89 | @staticmethod
90 | def resize_getitem(getitem_fn):
91 | """
92 | Decorator method that needs to be used around the ``__getitem__`` method. |br|
93 | This decorator enables the on the fly resizing of
94 | the ``input_dim`` with our :class:`~lightnet.data.DataLoader` class.
95 |
96 | Example:
97 | >>> class CustomSet(ln.data.Dataset):
98 | ... def __len__(self):
99 | ... return 10
100 | ... @ln.data.Dataset.resize_getitem
101 | ... def __getitem__(self, index):
102 | ... # Should return (image, anno) but here we return input_dim
103 | ... return self.input_dim
104 | >>> data = CustomSet((200,200))
105 | >>> data[0]
106 | (200, 200)
107 | >>> data[(480,320), 0]
108 | (480, 320)
109 | """
110 |
111 | @wraps(getitem_fn)
112 | def wrapper(self, index):
113 | if not isinstance(index, int):
114 | has_dim = True
115 | self._input_dim = index[0]
116 | self.enable_mosaic = index[2]
117 | index = index[1]
118 | else:
119 | has_dim = False
120 |
121 | ret_val = getitem_fn(self, index)
122 |
123 | if has_dim:
124 | del self._input_dim
125 |
126 | return ret_val
127 |
128 | return wrapper
129 |
--------------------------------------------------------------------------------
/exps/example/yolox_voc/yolox_yolo_s.py:
--------------------------------------------------------------------------------
1 | # encoding: utf-8
2 | import os
3 | import random
4 | import torch
5 | import torch.nn as nn
6 | import torch.distributed as dist
7 | from yolox.data import YOLODataset, ValTransform
8 | from yolox.data import YOLO_CLASSES
9 | from yolox.exp import Exp as MyExp
10 |
11 | class Exp(MyExp):
12 | def __init__(self):
13 | super(Exp, self).__init__()
14 | self.output_dir = "./output/YOLOX_outputs"
15 | self.print_interval = 10
16 | self.num_classes = len(YOLO_CLASSES)
17 | self.depth = 0.33
18 | self.width = 0.375 # min - 0.375 ; s - 0.50
19 | self.scale = (0.5, 1.5)
20 | self.random_size = (10, 20)
21 | self.eval_interval = 10
22 | self.input_size = (416, 416)
23 | self.test_size = (416, 416)
24 | self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]
25 | self.enable_mixup = False
26 |
27 | def get_data_loader(self, batch_size, is_distributed, no_aug=False):
28 | from yolox.data import (
29 | YOLODataset,
30 | TrainTransform,
31 | YoloBatchSampler,
32 | DataLoader,
33 | InfiniteSampler,
34 | MosaicDetection,
35 | )
36 |
37 | dataset = YOLODataset(
38 | data_dir='/home/xialuxi/work/dukto/data/yolov5face/yoloface/train_data',
39 | img_size=self.input_size,
40 | preproc=TrainTransform(
41 | rgb_means=(0.485, 0.456, 0.406),
42 | std=(0.229, 0.224, 0.225),
43 | max_labels=500,
44 | ),
45 | )
46 |
47 | dataset = MosaicDetection(
48 | dataset,
49 | mosaic=not no_aug,
50 | img_size=self.input_size,
51 | preproc=TrainTransform(
52 | rgb_means=(0.485, 0.456, 0.406),
53 | std=(0.229, 0.224, 0.225),
54 | max_labels=1000,
55 | ),
56 | degrees=self.degrees,
57 | translate=self.translate,
58 | scale=self.scale,
59 | shear=self.shear,
60 | perspective=self.perspective,
61 | enable_mixup=self.enable_mixup,
62 | )
63 |
64 | self.dataset = dataset
65 |
66 | if is_distributed:
67 | batch_size = batch_size // dist.get_world_size()
68 | sampler = InfiniteSampler(
69 | len(self.dataset), seed=self.seed if self.seed else 0
70 | )
71 | else:
72 | #sampler = torch.utils.data.RandomSampler(self.dataset)
73 | sampler = InfiniteSampler(len(self.dataset), seed=self.seed if self.seed else 0)
74 |
75 | batch_sampler = YoloBatchSampler(
76 | sampler=sampler,
77 | batch_size=batch_size,
78 | drop_last=False,
79 | input_dimension=self.input_size,
80 | mosaic=not no_aug,
81 | )
82 |
83 |
84 | dataloader_kwargs = {"num_workers": self.data_num_workers, "pin_memory": True}
85 | dataloader_kwargs["batch_sampler"] = batch_sampler
86 | train_loader = DataLoader(self.dataset, **dataloader_kwargs)
87 | return train_loader
88 |
89 | def get_eval_loader(self, batch_size, is_distributed, testdev=False):
90 |
91 | valdataset = YOLODataset(
92 | data_dir='/home/xialuxi/work/dukto/data/yolov5face/yoloface/train_data',
93 | train=False,
94 | img_size=self.test_size,
95 | preproc=ValTransform(
96 | rgb_means=(0.485, 0.456, 0.406),
97 | std=(0.229, 0.224, 0.225),
98 | ),
99 | )
100 |
101 | if is_distributed:
102 | batch_size = batch_size // dist.get_world_size()
103 | sampler = torch.utils.data.distributed.DistributedSampler(
104 | valdataset, shuffle=False
105 | )
106 | else:
107 | sampler = torch.utils.data.SequentialSampler(valdataset)
108 |
109 | dataloader_kwargs = {
110 | "num_workers": self.data_num_workers,
111 | "pin_memory": True,
112 | "sampler": sampler,
113 | }
114 | dataloader_kwargs["batch_size"] = batch_size
115 | #dataloader_kwargs["collate_fn"] =valdataset.collate_fn
116 | val_loader = torch.utils.data.DataLoader(valdataset, **dataloader_kwargs)
117 |
118 | return val_loader
119 |
120 | def get_evaluator(self, batch_size, is_distributed, testdev=False):
121 | from yolox.evaluators import VOCEvaluator, YOLOEvaluator
122 |
123 | val_loader = self.get_eval_loader(batch_size, is_distributed, testdev=testdev)
124 | evaluator = YOLOEvaluator(
125 | dataloader=val_loader,
126 | img_size=self.test_size,
127 | confthre=self.test_conf,
128 | nmsthre=self.nmsthre,
129 | num_classes=self.num_classes,
130 | )
131 | return evaluator
132 |
--------------------------------------------------------------------------------
/yolox/data/datasets/coco.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | import cv2
6 | import numpy as np
7 | from pycocotools.coco import COCO
8 |
9 | import os
10 |
11 | from ..dataloading import get_yolox_datadir
12 | from .datasets_wrapper import Dataset
13 |
14 |
15 | class COCODataset(Dataset):
16 | """
17 | COCO dataset class.
18 | """
19 |
20 | def __init__(
21 | self,
22 | data_dir=None,
23 | json_file="instances_train2017.json",
24 | name="train2017",
25 | img_size=(416, 416),
26 | preproc=None,
27 | ):
28 | """
29 | COCO dataset initialization. Annotation data are read into memory by COCO API.
30 | Args:
31 | data_dir (str): dataset root directory
32 | json_file (str): COCO json file name
33 | name (str): COCO data name (e.g. 'train2017' or 'val2017')
34 | img_size (int): target image size after pre-processing
35 | preproc: data augmentation strategy
36 | """
37 | super().__init__(img_size)
38 | if data_dir is None:
39 | data_dir = os.path.join(get_yolox_datadir(), "COCO")
40 | self.data_dir = data_dir
41 | self.json_file = json_file
42 |
43 | self.coco = COCO(os.path.join(self.data_dir, "annotations", self.json_file))
44 | self.ids = self.coco.getImgIds()
45 | self.class_ids = sorted(self.coco.getCatIds())
46 | cats = self.coco.loadCats(self.coco.getCatIds())
47 | self._classes = tuple([c["name"] for c in cats])
48 | self.annotations = self._load_coco_annotations()
49 | self.name = name
50 | self.img_size = img_size
51 | self.preproc = preproc
52 |
53 | def __len__(self):
54 | return len(self.ids)
55 |
56 | def _load_coco_annotations(self):
57 | return [self.load_anno_from_ids(_ids) for _ids in self.ids]
58 |
59 | def load_anno_from_ids(self, id_):
60 | im_ann = self.coco.loadImgs(id_)[0]
61 | width = im_ann["width"]
62 | height = im_ann["height"]
63 | anno_ids = self.coco.getAnnIds(imgIds=[int(id_)], iscrowd=False)
64 | annotations = self.coco.loadAnns(anno_ids)
65 | objs = []
66 | for obj in annotations:
67 | x1 = np.max((0, obj["bbox"][0]))
68 | y1 = np.max((0, obj["bbox"][1]))
69 | x2 = np.min((width - 1, x1 + np.max((0, obj["bbox"][2] - 1))))
70 | y2 = np.min((height - 1, y1 + np.max((0, obj["bbox"][3] - 1))))
71 | if obj["area"] > 0 and x2 >= x1 and y2 >= y1:
72 | obj["clean_bbox"] = [x1, y1, x2, y2]
73 | objs.append(obj)
74 |
75 | num_objs = len(objs)
76 |
77 | res = np.zeros((num_objs, 5))
78 |
79 | for ix, obj in enumerate(objs):
80 | cls = self.class_ids.index(obj["category_id"])
81 | res[ix, 0:4] = obj["clean_bbox"]
82 | res[ix, 4] = cls
83 |
84 | img_info = (height, width)
85 |
86 | file_name = im_ann["file_name"] if "file_name" in im_ann else "{:012}".format(id_) + ".jpg"
87 |
88 | del im_ann, annotations
89 |
90 | return (res, img_info, file_name)
91 |
92 | def load_anno(self, index):
93 | return self.annotations[index][0]
94 |
95 | def pull_item(self, index):
96 | id_ = self.ids[index]
97 |
98 | res, img_info, file_name = self.annotations[index]
99 | # load image and preprocess
100 | img_file = os.path.join(
101 | self.data_dir, self.name, file_name
102 | )
103 |
104 | img = cv2.imread(img_file)
105 | assert img is not None
106 |
107 | return img, res, img_info, np.array([id_])
108 |
109 | @Dataset.resize_getitem
110 | def __getitem__(self, index):
111 | """
112 | One image / label pair for the given index is picked up and pre-processed.
113 |
114 | Args:
115 | index (int): data index
116 |
117 | Returns:
118 | img (numpy.ndarray): pre-processed image
119 | padded_labels (torch.Tensor): pre-processed label data.
120 | The shape is :math:`[max_labels, 5]`.
121 | each label consists of [class, xc, yc, w, h]:
122 | class (float): class index.
123 | xc, yc (float) : center of bbox whose values range from 0 to 1.
124 | w, h (float) : size of bbox whose values range from 0 to 1.
125 | info_img : tuple of h, w, nh, nw, dx, dy.
126 | h, w (int): original shape of the image
127 | nh, nw (int): shape of the resized image without padding
128 | dx, dy (int): pad size
129 | img_id (int): same as the input index. Used for evaluation.
130 | """
131 | img, target, img_info, img_id = self.pull_item(index)
132 |
133 | if self.preproc is not None:
134 | img, target = self.preproc(img, target, self.input_dim)
135 | return img, target, img_info, img_id
136 |
--------------------------------------------------------------------------------
/yolox/data/datasets/yolo.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) Megvii, Inc. and its affiliates.
4 |
5 | import os
6 | import torch
7 | import cv2
8 | import numpy as np
9 | from ..dataloading import get_yolox_datadir
10 | from .datasets_wrapper import Dataset
11 | from .yolo_classes import YOLO_CLASSES
12 | from glob import glob
13 |
14 | IMAGE_EXT = ['.jpg', '.jpeg', '.webp', '.bmp', '.png']
15 |
16 | class YOLODataset(Dataset):
17 | """
18 | yolo dataset class.
19 | """
20 |
21 | def __init__(
22 | self,
23 | data_dir=None,
24 | train_images="images/train",
25 | val_images="images/val",
26 | train=True,
27 | img_size=(416, 416),
28 | preproc=None,
29 | cache_label=True
30 | ):
31 | super().__init__(img_size)
32 | if data_dir is None:
33 | data_dir = os.path.join(get_yolox_datadir(), "CVAT")
34 | self._classes = YOLO_CLASSES
35 | self.class_to_ind = dict(zip(YOLO_CLASSES, range(len(YOLO_CLASSES))))
36 | self.data_dir = data_dir
37 | self.train_images_dir = train_images
38 | self.val_images_dir = val_images
39 | self.img_size = img_size
40 | self.preproc = preproc
41 | self.cache_labels = cache_label
42 |
43 | if train:
44 | self.images_list = self.get_image_files(data_dir, self.train_images_dir)
45 | else:
46 | self.images_list = self.get_image_files(data_dir, self.val_images_dir)
47 |
48 | #for debug
49 | #self.images_list = self.images_list[0:200]
50 |
51 | self.labels_list = self.img2label_paths(self.images_list)
52 | #load all label
53 | self.all_labels = []
54 | if self.cache_labels:
55 | for i in range(len(self.labels_list)):
56 | one_label = self.load_anno(i)
57 | img_file = self.images_list[i]
58 | img = cv2.imread(img_file)
59 | assert img is not None
60 | h, w, c = img.shape
61 | one_label[:, 0:4] = self.convert(w, h, one_label[:, 0:4])
62 | one_label[:, -10:] = one_label[:, -10:] * [w, h, w, h, w, h, w, h, w, h]
63 | lmks_mask = one_label[:, -10:] > 0
64 | one_label[:, -10:] = one_label[:, -10:] * lmks_mask + lmks_mask - 1
65 | self.all_labels.append(one_label)
66 |
67 | def __len__(self):
68 | return len(self.images_list)
69 |
70 | def load_anno(self, index):
71 | #one_image = self.train_images_list[index]
72 | lb_file = self.labels_list[index]
73 | with open(lb_file, 'r') as f:
74 | l = np.array([x.split() for x in f.read().strip().splitlines()], dtype=np.float32) # labels
75 | if len(l):
76 | assert l.shape[1] == 15, 'labels require 5 columns each'
77 | #assert (l >= 0).all(), 'negative labels'
78 | assert (l[:, 1:] <= 1).all(), 'non-normalized or out of bounds coordinate labels'
79 | assert np.unique(l, axis=0).shape[0] == l.shape[0], 'duplicate labels'
80 | cls = l[:, 0]
81 | cls = np.expand_dims(cls, axis=1)
82 | box = l[:, 1:5]
83 | lmks = l[:, 5:]
84 | return np.concatenate((box, cls, lmks), axis = 1)
85 |
86 | def pull_item(self, index):
87 | img_file = self.images_list[index]
88 | img = cv2.imread(img_file)
89 | assert img is not None
90 | # load anno
91 | h, w, c = img.shape
92 | if self.cache_labels:
93 | res = self.all_labels[index]
94 | else:
95 | res = self.load_anno(index)
96 | res[:, 0:4] = self.convert(w, h, res[:, 0:4])
97 | img_info = (h, w)
98 | return img, res, img_info, index
99 |
100 | @Dataset.resize_getitem
101 | def __getitem__(self, index):
102 | img, target, img_info, img_id = self.pull_item(index)
103 | if self.preproc is not None:
104 | img, target = self.preproc(img, target, self.input_dim)
105 | return img, target, img_info, img_id
106 |
107 | # @staticmethod
108 | # def collate_fn(batch):
109 | # img, label, img_info, img_id = zip(*batch) # transposed
110 | # return torch.stack(img, 0), torch.cat(label, 0), torch.from_numpy(np.array(img_info)), torch.from_numpy(np.array(img_id))
111 |
112 | def img2label_paths(self, img_paths):
113 | # Define label paths as a function of image paths
114 | sa, sb = os.sep + 'images' + os.sep, os.sep + 'labels' + os.sep # /images/, /labels/ substrings
115 | return [x.replace(sa, sb, 1).replace('.' + x.split('.')[-1], '.txt') for x in img_paths]
116 |
117 | def get_image_files(self, path, dir):
118 | img_path = os.path.join(path, dir)
119 | all_file_list = []
120 | for ext in IMAGE_EXT:
121 | img_list = glob(os.path.join(img_path, '*' + ext))
122 | all_file_list.extend(img_list)
123 | return all_file_list
124 |
125 | def convert(self, w, h , box):
126 | box = box * [w, h, w, h]
127 | x1 = box[:, 0] - 0.5 * box[:, 2]
128 | y1 = box[:, 1] - 0.5 * box[:, 3]
129 | x2 = box[:, 0] + 0.5 * box[:, 2]
130 | y2 = box[:, 1] + 0.5 * box[:, 3]
131 | x1 = np.expand_dims(x1, axis=1)
132 | y1 = np.expand_dims(y1, axis=1)
133 | x2 = np.expand_dims(x2, axis=1)
134 | y2 = np.expand_dims(y2, axis=1)
135 | return np.concatenate((x1, y1, x2, y2), axis = 1)
136 |
--------------------------------------------------------------------------------
/yolox/utils/boxes.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import numpy as np
6 |
7 | import torch
8 | import torchvision
9 |
10 | __all__ = [
11 | "filter_box",
12 | "postprocess",
13 | "bboxes_iou",
14 | "matrix_iou",
15 | "adjust_box_anns",
16 | "adjust_lmks_anns",
17 | "xyxy2xywh",
18 | "xyxy2cxcywh",
19 | ]
20 |
21 |
22 | def filter_box(output, scale_range):
23 | """
24 | output: (N, 5+class) shape
25 | """
26 | min_scale, max_scale = scale_range
27 | w = output[:, 2] - output[:, 0]
28 | h = output[:, 3] - output[:, 1]
29 | keep = (w * h > min_scale * min_scale) & (w * h < max_scale * max_scale)
30 | return output[keep]
31 |
32 |
33 | def postprocess(prediction, num_classes, conf_thre=0.7, nms_thre=0.45):
34 | box_corner = prediction.new(prediction.shape)
35 | box_corner[:, :, 0] = prediction[:, :, 0] - prediction[:, :, 2] / 2
36 | box_corner[:, :, 1] = prediction[:, :, 1] - prediction[:, :, 3] / 2
37 | box_corner[:, :, 2] = prediction[:, :, 0] + prediction[:, :, 2] / 2
38 | box_corner[:, :, 3] = prediction[:, :, 1] + prediction[:, :, 3] / 2
39 | prediction[:, :, :4] = box_corner[:, :, :4]
40 |
41 | output = [None for _ in range(len(prediction))]
42 | for i, image_pred in enumerate(prediction):
43 |
44 | # If none are remaining => process next image
45 | if not image_pred.size(0):
46 | continue
47 | # Get score and class with highest confidence
48 | class_conf, class_pred = torch.max(
49 | image_pred[:, 5 : 5 + num_classes], 1, keepdim=True
50 | )
51 |
52 | conf_mask = (image_pred[:, 4] * class_conf.squeeze() >= conf_thre).squeeze()
53 | # _, conf_mask = torch.topk((image_pred[:, 4] * class_conf.squeeze()), 1000)
54 | # Detections ordered as (x1, y1, x2, y2, obj_conf, class_conf, class_pred)
55 | detections = torch.cat((image_pred[:, :5], class_conf, class_pred.float(), image_pred[:, 5 + num_classes:]), 1)
56 | detections = detections[conf_mask]
57 | if not detections.size(0):
58 | continue
59 |
60 | nms_out_index = torchvision.ops.batched_nms(
61 | detections[:, :4],
62 | detections[:, 4] * detections[:, 5],
63 | detections[:, 6],
64 | nms_thre,
65 | )
66 | detections = detections[nms_out_index]
67 | if output[i] is None:
68 | output[i] = detections
69 | else:
70 | output[i] = torch.cat((output[i], detections))
71 |
72 | return output
73 |
74 |
75 | def bboxes_iou(bboxes_a, bboxes_b, xyxy=True):
76 | if bboxes_a.shape[1] != 4 or bboxes_b.shape[1] != 4:
77 | raise IndexError
78 |
79 | if xyxy:
80 | tl = torch.max(bboxes_a[:, None, :2], bboxes_b[:, :2])
81 | br = torch.min(bboxes_a[:, None, 2:], bboxes_b[:, 2:])
82 | area_a = torch.prod(bboxes_a[:, 2:] - bboxes_a[:, :2], 1)
83 | area_b = torch.prod(bboxes_b[:, 2:] - bboxes_b[:, :2], 1)
84 | else:
85 | tl = torch.max(
86 | (bboxes_a[:, None, :2] - bboxes_a[:, None, 2:] / 2),
87 | (bboxes_b[:, :2] - bboxes_b[:, 2:] / 2),
88 | )
89 | br = torch.min(
90 | (bboxes_a[:, None, :2] + bboxes_a[:, None, 2:] / 2),
91 | (bboxes_b[:, :2] + bboxes_b[:, 2:] / 2),
92 | )
93 |
94 | area_a = torch.prod(bboxes_a[:, 2:], 1)
95 | area_b = torch.prod(bboxes_b[:, 2:], 1)
96 | en = (tl < br).type(tl.type()).prod(dim=2)
97 | area_i = torch.prod(br - tl, 2) * en # * ((tl < br).all())
98 | return area_i / (area_a[:, None] + area_b - area_i)
99 |
100 |
101 | def matrix_iou(a, b):
102 | """
103 | return iou of a and b, numpy version for data augenmentation
104 | """
105 | lt = np.maximum(a[:, np.newaxis, :2], b[:, :2])
106 | rb = np.minimum(a[:, np.newaxis, 2:], b[:, 2:])
107 |
108 | area_i = np.prod(rb - lt, axis=2) * (lt < rb).all(axis=2)
109 | area_a = np.prod(a[:, 2:] - a[:, :2], axis=1)
110 | area_b = np.prod(b[:, 2:] - b[:, :2], axis=1)
111 | return area_i / (area_a[:, np.newaxis] + area_b - area_i + 1e-12)
112 |
113 |
114 | def adjust_box_anns(bbox, scale_ratio, padw, padh, w_max, h_max):
115 | bbox[:, 0::2] = np.clip(bbox[:, 0::2] * scale_ratio + padw, 0, w_max)
116 | bbox[:, 1::2] = np.clip(bbox[:, 1::2] * scale_ratio + padh, 0, h_max)
117 | return bbox
118 |
119 | def adjust_lmks_anns(landmarks, scale_ratio, padw, padh, w_max, h_max):
120 | mask_landmarks = np.array(landmarks > 0, dtype=np.int32)
121 | landmarks[:, 0::2] = landmarks[:, 0::2] * scale_ratio + padw
122 | landmarks[:, 1::2] = landmarks[:, 1::2] * scale_ratio + padh
123 | landmarks = landmarks * mask_landmarks + mask_landmarks - 1
124 | for j, points5 in enumerate(landmarks):
125 | points5 = points5.reshape(-1, 2)
126 | for k, point in enumerate(points5):
127 | x, y = point
128 | if x < 0 or y < 0 or x > w_max or y > h_max:
129 | landmarks[j][2 * k] = -1
130 | landmarks[j][2 * k + 1] = -1
131 | return landmarks
132 |
133 |
134 | def xyxy2xywh(bboxes):
135 | bboxes[:, 2] = bboxes[:, 2] - bboxes[:, 0]
136 | bboxes[:, 3] = bboxes[:, 3] - bboxes[:, 1]
137 | return bboxes
138 |
139 |
140 | def xyxy2cxcywh(bboxes):
141 | bboxes[:, 2] = bboxes[:, 2] - bboxes[:, 0]
142 | bboxes[:, 3] = bboxes[:, 3] - bboxes[:, 1]
143 | bboxes[:, 0] = bboxes[:, 0] + bboxes[:, 2] * 0.5
144 | bboxes[:, 1] = bboxes[:, 1] + bboxes[:, 3] * 0.5
145 | return bboxes
146 |
--------------------------------------------------------------------------------
/docs/train_custom_data.md:
--------------------------------------------------------------------------------
1 | # Train Custom Data.
2 | This page explains how to train your own custom data with YOLOX.
3 |
4 | We take an example of fine-tuning YOLOX-S model on VOC dataset to give a more clear guide.
5 |
6 | ## 0. Before you start
7 | Clone this repo and follow the [README](../README.md) to install YOLOX.
8 |
9 | ## 1. Create your own dataset
10 | **Step 1** Prepare your own dataset with images and labels first. For labeling images, you can use tools like [Labelme](https://github.com/wkentaro/labelme) or [CVAT](https://github.com/openvinotoolkit/cvat).
11 |
12 | **Step 2** Then, you should write the corresponding Dataset Class which can load images and labels through `__getitem__` method. We currently support COCO format and VOC format.
13 |
14 | You can also write the Dataset by your own. Let's take the [VOC](../yolox/data/datasets/voc.py#L151) Dataset file for example:
15 | ```python
16 | @Dataset.resize_getitem
17 | def __getitem__(self, index):
18 | img, target, img_info, img_id = self.pull_item(index)
19 |
20 | if self.preproc is not None:
21 | img, target = self.preproc(img, target, self.input_dim)
22 |
23 | return img, target, img_info, img_id
24 | ```
25 |
26 | One more thing worth noting is that you should also implement [pull_item](../yolox/data/datasets/voc.py#L129) and [load_anno](../yolox/data/datasets/voc.py#L121) method for the `Mosiac` and `MixUp` augmentations.
27 |
28 | **Step 3** Prepare the evaluator. We currently have [COCO evaluator](../yolox/evaluators/coco_evaluator.py) and [VOC evaluator](../yolox/evaluators/voc_evaluator.py).
29 | If you have your own format data or evaluation metric, you can write your own evaluator.
30 |
31 | **Step 4** Put your dataset under `$YOLOX_DIR/datasets`, for VOC:
32 |
33 | ```shell
34 | ln -s /path/to/your/VOCdevkit ./datasets/VOCdevkit
35 | ```
36 | * The path "VOCdevkit" will be used in your exp file described in next section. Specifically, in `get_data_loader` and `get_eval_loader` function.
37 |
38 | ## 2. Create your Exp file to control everything
39 | We put everything involved in a model to one single Exp file, including model setting, training setting, and testing setting.
40 |
41 | **A complete Exp file is at [yolox_base.py](../yolox/exp/yolox_base.py).** It may be too long to write for every exp, but you can inherit the base Exp file and only overwrite the changed part.
42 |
43 | Let's take the [VOC Exp file](../exps/example/yolox_voc/yolox_voc_s.py) as an example.
44 |
45 | We select `YOLOX-S` model here, so we should change the network depth and width. VOC has only 20 classes, so we should also change the `num_classes`.
46 |
47 | These configs are changed in the `init()` method:
48 | ```python
49 | class Exp(MyExp):
50 | def __init__(self):
51 | super(Exp, self).__init__()
52 | self.num_classes = 20
53 | self.depth = 0.33
54 | self.width = 0.50
55 | self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]
56 | ```
57 |
58 | Besides, you should also overwrite the `dataset` and `evaluator`, prepared before training the model on your own data.
59 |
60 | Please see [get_data_loader](../exps/example/yolox_voc/yolox_voc_s.py#L20), [get_eval_loader](../exps/example/yolox_voc/yolox_voc_s.py#L82), and [get_evaluator](../exps/example/yolox_voc/yolox_voc_s.py#L113) for more details.
61 |
62 | ## 3. Train
63 | Except special cases, we always recommend to use our [COCO pretrained weights](../README.md) for initializing the model.
64 |
65 | Once you get the Exp file and the COCO pretrained weights we provided, you can train your own model by the following below command:
66 | ```bash
67 | python tools/train.py -f /path/to/your/Exp/file -d 8 -b 64 --fp16 -o -c /path/to/the/pretrained/weights
68 | ```
69 |
70 | or take the `YOLOX-S` VOC training for example:
71 | ```bash
72 | python tools/train.py -f exps/example/yolox_voc/yolox_voc_s.py -d 8 -b 64 --fp16 -o -c /path/to/yolox_s.pth.tar
73 | ```
74 |
75 | (Don't worry for the different shape of detection head between the pretrained weights and your own model, we will handle it)
76 |
77 | ## 4. Tips for Best Training Results
78 |
79 | As **YOLOX** is an anchor-free detector with only several hyper-parameters, most of the time good results can be obtained with no changes to the models or training settings.
80 | We thus always recommend you first train with all default training settings.
81 |
82 | If at first you don't get good results, there are steps you could consider to improve the model.
83 |
84 | **Model Selection** We provide `YOLOX-Nano`, `YOLOX-Tiny`, and `YOLOX-S` for mobile deployments, while `YOLOX-M`/`L`/`X` for cloud or high performance GPU deployments.
85 |
86 | If your deployment meets any compatibility issues. we recommend `YOLOX-DarkNet53`.
87 |
88 | **Training Configs** If your training overfits early, then you can reduce max\_epochs or decrease the base\_lr and min\_lr\_ratio in your Exp file:
89 |
90 | ```python
91 | # -------------- training config --------------------- #
92 | self.warmup_epochs = 5
93 | self.max_epoch = 300
94 | self.warmup_lr = 0
95 | self.basic_lr_per_img = 0.01 / 64.0
96 | self.scheduler = "yoloxwarmcos"
97 | self.no_aug_epochs = 15
98 | self.min_lr_ratio = 0.05
99 | self.ema = True
100 |
101 | self.weight_decay = 5e-4
102 | self.momentum = 0.9
103 | ```
104 |
105 | **Aug Configs** You may also change the degree of the augmentations.
106 |
107 | Generally, for small models, you should weak the aug, while for large models or small size of dataset, you may enchance the aug in your Exp file:
108 | ```python
109 | # --------------- transform config ----------------- #
110 | self.degrees = 10.0
111 | self.translate = 0.1
112 | self.scale = (0.1, 2)
113 | self.mscale = (0.8, 1.6)
114 | self.shear = 2.0
115 | self.perspective = 0.0
116 | self.enable_mixup = True
117 | ```
118 |
119 | **Design your own detector** You may refer to our [Arxiv](https://arxiv.org/abs/2107.08430) paper for details and suggestions for designing your own detector.
120 |
--------------------------------------------------------------------------------
/demo/ncnn/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/demo/MegEngine/python/models/darknet.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- encoding: utf-8 -*-
3 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
4 |
5 | import megengine.module as M
6 |
7 | from .network_blocks import BaseConv, CSPLayer, DWConv, Focus, ResLayer, SPPBottleneck
8 |
9 |
10 | class Darknet(M.Module):
11 | # number of blocks from dark2 to dark5.
12 | depth2blocks = {21: [1, 2, 2, 1], 53: [2, 8, 8, 4]}
13 |
14 | def __init__(
15 | self, depth, in_channels=3, stem_out_channels=32, out_features=("dark3", "dark4", "dark5"),
16 | ):
17 | """
18 | Args:
19 | depth (int): depth of darknet used in model, usually use [21, 53] for this param.
20 | in_channels (int): number of input channels, for example, use 3 for RGB image.
21 | stem_out_channels (int): number of output chanels of darknet stem.
22 | It decides channels of darknet layer2 to layer5.
23 | out_features (Tuple[str]): desired output layer name.
24 | """
25 | super().__init__()
26 | assert out_features, "please provide output features of Darknet"
27 | self.out_features = out_features
28 | self.stem = M.Sequential(
29 | BaseConv(in_channels, stem_out_channels, ksize=3, stride=1, act="lrelu"),
30 | *self.make_group_layer(stem_out_channels, num_blocks=1, stride=2),
31 | )
32 | in_channels = stem_out_channels * 2 # 64
33 |
34 | num_blocks = Darknet.depth2blocks[depth]
35 | # create darknet with `stem_out_channels` and `num_blocks` layers.
36 | # to make model structure more clear, we don't use `for` statement in python.
37 | self.dark2 = M.Sequential(*self.make_group_layer(in_channels, num_blocks[0], stride=2))
38 | in_channels *= 2 # 128
39 | self.dark3 = M.Sequential(*self.make_group_layer(in_channels, num_blocks[1], stride=2))
40 | in_channels *= 2 # 256
41 | self.dark4 = M.Sequential(*self.make_group_layer(in_channels, num_blocks[2], stride=2))
42 | in_channels *= 2 # 512
43 |
44 | self.dark5 = M.Sequential(
45 | *self.make_group_layer(in_channels, num_blocks[3], stride=2),
46 | *self.make_spp_block([in_channels, in_channels * 2], in_channels * 2),
47 | )
48 |
49 | def make_group_layer(self, in_channels: int, num_blocks: int, stride: int = 1):
50 | "starts with conv layer then has `num_blocks` `ResLayer`"
51 | return [
52 | BaseConv(in_channels, in_channels * 2, ksize=3, stride=stride, act="lrelu"),
53 | *[(ResLayer(in_channels * 2)) for _ in range(num_blocks)]
54 | ]
55 |
56 | def make_spp_block(self, filters_list, in_filters):
57 | m = M.Sequential(
58 | *[
59 | BaseConv(in_filters, filters_list[0], 1, stride=1, act="lrelu"),
60 | BaseConv(filters_list[0], filters_list[1], 3, stride=1, act="lrelu"),
61 | SPPBottleneck(
62 | in_channels=filters_list[1],
63 | out_channels=filters_list[0],
64 | activation="lrelu"
65 | ),
66 | BaseConv(filters_list[0], filters_list[1], 3, stride=1, act="lrelu"),
67 | BaseConv(filters_list[1], filters_list[0], 1, stride=1, act="lrelu"),
68 | ]
69 | )
70 | return m
71 |
72 | def forward(self, x):
73 | outputs = {}
74 | x = self.stem(x)
75 | outputs["stem"] = x
76 | x = self.dark2(x)
77 | outputs["dark2"] = x
78 | x = self.dark3(x)
79 | outputs["dark3"] = x
80 | x = self.dark4(x)
81 | outputs["dark4"] = x
82 | x = self.dark5(x)
83 | outputs["dark5"] = x
84 | return {k: v for k, v in outputs.items() if k in self.out_features}
85 |
86 |
87 | class CSPDarknet(M.Module):
88 |
89 | def __init__(
90 | self, dep_mul, wid_mul,
91 | out_features=("dark3", "dark4", "dark5"),
92 | depthwise=False, act="silu",
93 | ):
94 | super().__init__()
95 | assert out_features, "please provide output features of Darknet"
96 | self.out_features = out_features
97 | Conv = DWConv if depthwise else BaseConv
98 |
99 | base_channels = int(wid_mul * 64) # 64
100 | base_depth = max(round(dep_mul * 3), 1) # 3
101 |
102 | # stem
103 | self.stem = Focus(3, base_channels, ksize=3, act=act)
104 |
105 | # dark2
106 | self.dark2 = M.Sequential(
107 | Conv(base_channels, base_channels * 2, 3, 2, act=act),
108 | CSPLayer(
109 | base_channels * 2, base_channels * 2,
110 | n=base_depth, depthwise=depthwise, act=act
111 | ),
112 | )
113 |
114 | # dark3
115 | self.dark3 = M.Sequential(
116 | Conv(base_channels * 2, base_channels * 4, 3, 2, act=act),
117 | CSPLayer(
118 | base_channels * 4, base_channels * 4,
119 | n=base_depth * 3, depthwise=depthwise, act=act,
120 | ),
121 | )
122 |
123 | # dark4
124 | self.dark4 = M.Sequential(
125 | Conv(base_channels * 4, base_channels * 8, 3, 2, act=act),
126 | CSPLayer(
127 | base_channels * 8, base_channels * 8,
128 | n=base_depth * 3, depthwise=depthwise, act=act,
129 | ),
130 | )
131 |
132 | # dark5
133 | self.dark5 = M.Sequential(
134 | Conv(base_channels * 8, base_channels * 16, 3, 2, act=act),
135 | SPPBottleneck(base_channels * 16, base_channels * 16, activation=act),
136 | CSPLayer(
137 | base_channels * 16, base_channels * 16, n=base_depth,
138 | shortcut=False, depthwise=depthwise, act=act,
139 | ),
140 | )
141 |
142 | def forward(self, x):
143 | outputs = {}
144 | x = self.stem(x)
145 | outputs["stem"] = x
146 | x = self.dark2(x)
147 | outputs["dark2"] = x
148 | x = self.dark3(x)
149 | outputs["dark3"] = x
150 | x = self.dark4(x)
151 | outputs["dark4"] = x
152 | x = self.dark5(x)
153 | outputs["dark5"] = x
154 | return {k: v for k, v in outputs.items() if k in self.out_features}
155 |
--------------------------------------------------------------------------------
/yolox/layers/fast_coco_eval_api.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding:utf-8 -*-
3 | # This file comes from
4 | # https://github.com/facebookresearch/detectron2/blob/master/detectron2/evaluation/fast_eval_api.py
5 | # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
6 | # Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
7 |
8 | import numpy as np
9 | from pycocotools.cocoeval import COCOeval
10 |
11 | # import torch first to make yolox._C work without ImportError of libc10.so
12 | # in YOLOX, env is already set in __init__.py.
13 | from yolox import _C
14 |
15 | import copy
16 | import time
17 |
18 |
19 | class COCOeval_opt(COCOeval):
20 | """
21 | This is a slightly modified version of the original COCO API, where the functions evaluateImg()
22 | and accumulate() are implemented in C++ to speedup evaluation
23 | """
24 |
25 | def evaluate(self):
26 | """
27 | Run per image evaluation on given images and store results in self.evalImgs_cpp, a
28 | datastructure that isn't readable from Python but is used by a c++ implementation of
29 | accumulate(). Unlike the original COCO PythonAPI, we don't populate the datastructure
30 | self.evalImgs because this datastructure is a computational bottleneck.
31 | :return: None
32 | """
33 | tic = time.time()
34 |
35 | print("Running per image evaluation...")
36 | p = self.params
37 | # add backward compatibility if useSegm is specified in params
38 | if p.useSegm is not None:
39 | p.iouType = "segm" if p.useSegm == 1 else "bbox"
40 | print(
41 | "useSegm (deprecated) is not None. Running {} evaluation".format(
42 | p.iouType
43 | )
44 | )
45 | print("Evaluate annotation type *{}*".format(p.iouType))
46 | p.imgIds = list(np.unique(p.imgIds))
47 | if p.useCats:
48 | p.catIds = list(np.unique(p.catIds))
49 | p.maxDets = sorted(p.maxDets)
50 | self.params = p
51 |
52 | self._prepare()
53 |
54 | # loop through images, area range, max detection number
55 | catIds = p.catIds if p.useCats else [-1]
56 |
57 | if p.iouType == "segm" or p.iouType == "bbox":
58 | computeIoU = self.computeIoU
59 | elif p.iouType == "keypoints":
60 | computeIoU = self.computeOks
61 | self.ious = {
62 | (imgId, catId): computeIoU(imgId, catId)
63 | for imgId in p.imgIds
64 | for catId in catIds
65 | }
66 |
67 | maxDet = p.maxDets[-1]
68 |
69 | # <<<< Beginning of code differences with original COCO API
70 | def convert_instances_to_cpp(instances, is_det=False):
71 | # Convert annotations for a list of instances in an image to a format that's fast
72 | # to access in C++
73 | instances_cpp = []
74 | for instance in instances:
75 | instance_cpp = _C.InstanceAnnotation(
76 | int(instance["id"]),
77 | instance["score"] if is_det else instance.get("score", 0.0),
78 | instance["area"],
79 | bool(instance.get("iscrowd", 0)),
80 | bool(instance.get("ignore", 0)),
81 | )
82 | instances_cpp.append(instance_cpp)
83 | return instances_cpp
84 |
85 | # Convert GT annotations, detections, and IOUs to a format that's fast to access in C++
86 | ground_truth_instances = [
87 | [convert_instances_to_cpp(self._gts[imgId, catId]) for catId in p.catIds]
88 | for imgId in p.imgIds
89 | ]
90 | detected_instances = [
91 | [
92 | convert_instances_to_cpp(self._dts[imgId, catId], is_det=True)
93 | for catId in p.catIds
94 | ]
95 | for imgId in p.imgIds
96 | ]
97 | ious = [[self.ious[imgId, catId] for catId in catIds] for imgId in p.imgIds]
98 |
99 | if not p.useCats:
100 | # For each image, flatten per-category lists into a single list
101 | ground_truth_instances = [
102 | [[o for c in i for o in c]] for i in ground_truth_instances
103 | ]
104 | detected_instances = [
105 | [[o for c in i for o in c]] for i in detected_instances
106 | ]
107 |
108 | # Call C++ implementation of self.evaluateImgs()
109 | self._evalImgs_cpp = _C.COCOevalEvaluateImages(
110 | p.areaRng,
111 | maxDet,
112 | p.iouThrs,
113 | ious,
114 | ground_truth_instances,
115 | detected_instances,
116 | )
117 | self._evalImgs = None
118 |
119 | self._paramsEval = copy.deepcopy(self.params)
120 | toc = time.time()
121 | print("COCOeval_opt.evaluate() finished in {:0.2f} seconds.".format(toc - tic))
122 | # >>>> End of code differences with original COCO API
123 |
124 | def accumulate(self):
125 | """
126 | Accumulate per image evaluation results and store the result in self.eval. Does not
127 | support changing parameter settings from those used by self.evaluate()
128 | """
129 | print("Accumulating evaluation results...")
130 | tic = time.time()
131 | if not hasattr(self, "_evalImgs_cpp"):
132 | print("Please run evaluate() first")
133 |
134 | self.eval = _C.COCOevalAccumulate(self._paramsEval, self._evalImgs_cpp)
135 |
136 | # recall is num_iou_thresholds X num_categories X num_area_ranges X num_max_detections
137 | self.eval["recall"] = np.array(self.eval["recall"]).reshape(
138 | self.eval["counts"][:1] + self.eval["counts"][2:]
139 | )
140 |
141 | # precision and scores are num_iou_thresholds X num_recall_thresholds X num_categories X
142 | # num_area_ranges X num_max_detections
143 | self.eval["precision"] = np.array(self.eval["precision"]).reshape(
144 | self.eval["counts"]
145 | )
146 | self.eval["scores"] = np.array(self.eval["scores"]).reshape(self.eval["counts"])
147 | toc = time.time()
148 | print(
149 | "COCOeval_opt.accumulate() finished in {:0.2f} seconds.".format(toc - tic)
150 | )
151 |
--------------------------------------------------------------------------------
/demo/OpenVINO/python/openvino_inference.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # -*- coding: utf-8 -*-
3 | # Copyright (C) 2018-2021 Intel Corporation
4 | # SPDX-License-Identifier: Apache-2.0
5 | # Copyright (c) Megvii, Inc. and its affiliates.
6 |
7 | import argparse
8 | import logging as log
9 | import os
10 | import sys
11 |
12 | import cv2
13 | import numpy as np
14 |
15 | from openvino.inference_engine import IECore
16 |
17 | from yolox.data.data_augment import preproc as preprocess
18 | from yolox.data.datasets import COCO_CLASSES
19 | from yolox.utils import mkdir, multiclass_nms, demo_postprocess, vis
20 |
21 |
22 | def parse_args() -> argparse.Namespace:
23 | """Parse and return command line arguments"""
24 | parser = argparse.ArgumentParser(add_help=False)
25 | args = parser.add_argument_group('Options')
26 | args.add_argument(
27 | '-h',
28 | '--help',
29 | action='help',
30 | help='Show this help message and exit.')
31 | args.add_argument(
32 | '-m',
33 | '--model',
34 | required=True,
35 | type=str,
36 | help='Required. Path to an .xml or .onnx file with a trained model.')
37 | args.add_argument(
38 | '-i',
39 | '--input',
40 | required=True,
41 | type=str,
42 | help='Required. Path to an image file.')
43 | args.add_argument(
44 | '-o',
45 | '--output_dir',
46 | type=str,
47 | default='demo_output',
48 | help='Path to your output dir.')
49 | args.add_argument(
50 | '-s',
51 | '--score_thr',
52 | type=float,
53 | default=0.3,
54 | help="Score threshould to visualize the result.")
55 | args.add_argument(
56 | '-d',
57 | '--device',
58 | default='CPU',
59 | type=str,
60 | help='Optional. Specify the target device to infer on; CPU, GPU, \
61 | MYRIAD, HDDL or HETERO: is acceptable. The sample will look \
62 | for a suitable plugin for device specified. Default value \
63 | is CPU.')
64 | args.add_argument(
65 | '--labels',
66 | default=None,
67 | type=str,
68 | help='Option:al. Path to a labels mapping file.')
69 | args.add_argument(
70 | '-nt',
71 | '--number_top',
72 | default=10,
73 | type=int,
74 | help='Optional. Number of top results.')
75 | return parser.parse_args()
76 |
77 |
78 | def main():
79 | log.basicConfig(format='[ %(levelname)s ] %(message)s', level=log.INFO, stream=sys.stdout)
80 | args = parse_args()
81 |
82 | # ---------------------------Step 1. Initialize inference engine core--------------------------------------------------
83 | log.info('Creating Inference Engine')
84 | ie = IECore()
85 |
86 | # ---------------------------Step 2. Read a model in OpenVINO Intermediate Representation or ONNX format---------------
87 | log.info(f'Reading the network: {args.model}')
88 | # (.xml and .bin files) or (.onnx file)
89 | net = ie.read_network(model=args.model)
90 |
91 | if len(net.input_info) != 1:
92 | log.error('Sample supports only single input topologies')
93 | return -1
94 | if len(net.outputs) != 1:
95 | log.error('Sample supports only single output topologies')
96 | return -1
97 |
98 | # ---------------------------Step 3. Configure input & output----------------------------------------------------------
99 | log.info('Configuring input and output blobs')
100 | # Get names of input and output blobs
101 | input_blob = next(iter(net.input_info))
102 | out_blob = next(iter(net.outputs))
103 |
104 | # Set input and output precision manually
105 | net.input_info[input_blob].precision = 'FP32'
106 | net.outputs[out_blob].precision = 'FP16'
107 |
108 | # Get a number of classes recognized by a model
109 | num_of_classes = max(net.outputs[out_blob].shape)
110 |
111 | # ---------------------------Step 4. Loading model to the device-------------------------------------------------------
112 | log.info('Loading the model to the plugin')
113 | exec_net = ie.load_network(network=net, device_name=args.device)
114 |
115 | # ---------------------------Step 5. Create infer request--------------------------------------------------------------
116 | # load_network() method of the IECore class with a specified number of requests (default 1) returns an ExecutableNetwork
117 | # instance which stores infer requests. So you already created Infer requests in the previous step.
118 |
119 | # ---------------------------Step 6. Prepare input---------------------------------------------------------------------
120 | origin_img = cv2.imread(args.input)
121 | _, _, h, w = net.input_info[input_blob].input_data.shape
122 | mean = (0.485, 0.456, 0.406)
123 | std = (0.229, 0.224, 0.225)
124 | image, ratio = preprocess(origin_img, (h, w), mean, std)
125 |
126 | # ---------------------------Step 7. Do inference----------------------------------------------------------------------
127 | log.info('Starting inference in synchronous mode')
128 | res = exec_net.infer(inputs={input_blob: image})
129 |
130 | # ---------------------------Step 8. Process output--------------------------------------------------------------------
131 | res = res[out_blob]
132 |
133 | predictions = demo_postprocess(res, (h, w), p6=False)[0]
134 |
135 | boxes = predictions[:, :4]
136 | scores = predictions[:, 4, None] * predictions[:, 5:]
137 |
138 | boxes_xyxy = np.ones_like(boxes)
139 | boxes_xyxy[:, 0] = boxes[:, 0] - boxes[:, 2]/2.
140 | boxes_xyxy[:, 1] = boxes[:, 1] - boxes[:, 3]/2.
141 | boxes_xyxy[:, 2] = boxes[:, 0] + boxes[:, 2]/2.
142 | boxes_xyxy[:, 3] = boxes[:, 1] + boxes[:, 3]/2.
143 | boxes_xyxy /= ratio
144 | dets = multiclass_nms(boxes_xyxy, scores, nms_thr=0.45, score_thr=0.1)
145 |
146 | if dets is not None:
147 | final_boxes = dets[:, :4]
148 | final_scores, final_cls_inds = dets[:, 4], dets[:, 5]
149 | origin_img = vis(origin_img, final_boxes, final_scores, final_cls_inds,
150 | conf=args.score_thr, class_names=COCO_CLASSES)
151 |
152 | mkdir(args.output_dir)
153 | output_path = os.path.join(args.output_dir, args.input.split("/")[-1])
154 | cv2.imwrite(output_path, origin_img)
155 |
156 |
157 | if __name__ == '__main__':
158 | sys.exit(main())
159 |
--------------------------------------------------------------------------------